<repo_name>androlua/javaide<file_sep>/common/src/main/java/com/jecelyin/common/app/JecApp.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.common.app;

import android.app.Application;
import android.content.Context;
import android.os.StrictMode;

import com.jecelyin.common.utils.L;
import com.jecelyin.common.utils.SysUtils;

/**
 * @author <NAME> <<EMAIL>>
 */
public abstract class JecApp extends Application  {
    private static Context context;
    private static long startupTimestamp;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();

        if (startupTimestamp == 0)
            startupTimestamp = System.currentTimeMillis();

        if (SysUtils.isDebug(this))
        {
            L.debug = true;
            //内存泄漏监控
            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
            builder.detectAll();
            builder.penaltyLog();
            StrictMode.setVmPolicy(builder.build());
        }
        installMonitor();
    }



    public static Context getContext() {
        return context;
    }

    public static long getStartupTimestamp() {
        return startupTimestamp;
    }

    abstract protected void installMonitor();

    abstract public void watch(Object object);
}
<file_sep>/app/src/main/java/com/duy/compile/CompileManager.java
/*
 *  Copyright (c) 2017 <NAME>
 *
 * 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.compile;

import android.app.Activity;
import android.content.Intent;

import com.duy.compile.external.CompileHelper;
import com.duy.ide.debug.activities.DebugActivity;
import com.duy.ide.editor.code.MainActivity;
import com.duy.project.file.java.JavaProjectFolder;
import com.duy.run.activities.ExecuteActivity;

import java.io.File;

/**
 * Created by Duy on 11-Feb-17.
 */

public class CompileManager {

    public static final String FILE_PATH = "file_name";     // extras indicators
    public static final String IS_NEW = "is_new";
    public static final String INITIAL_POS = "initial_pos";
    public static final int ACTIVITY_EDITOR = 1001;
    public static final String MODE = "run_mode";

    public static final String PROJECT_FILE = "project_file";
    public static final String ACTION = "action";
    public static final String ARGS = "program_args";
    public static final String DEX_FILE = "dex_path";

    private final Activity mActivity;

    public CompileManager(Activity activity) {
        this.mActivity = activity;
    }



    public void debug(String name) {
        Intent intent = new Intent(mActivity, DebugActivity.class);
        intent.putExtra(FILE_PATH, name);
        mActivity.startActivity(intent);
    }

    public void edit(String fileName, Boolean isNew) {
        Intent intent = new Intent(mActivity, MainActivity.class);
        intent.putExtra(FILE_PATH, fileName);
        intent.putExtra(IS_NEW, isNew);
        intent.putExtra(INITIAL_POS, 0);
        mActivity.startActivityForResult(intent, ACTIVITY_EDITOR);
    }


    public void executeDex(JavaProjectFolder projectFile, File dex) {
        Intent intent = new Intent(mActivity, ExecuteActivity.class);
        intent.putExtra(ACTION, CompileHelper.Action.RUN_DEX);
        intent.putExtra(PROJECT_FILE, projectFile);
        intent.putExtra(DEX_FILE, dex);
        mActivity.startActivity(intent);
    }

    public void buildApk() {

    }

    public interface ProcessCallback {
        void onFailed(String s);
    }
}
<file_sep>/app/src/main/java/com/duy/ide/code_sample/fragments/SelectProjectFragment.java
package com.duy.ide.code_sample.fragments;


import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.duy.ide.R;
import com.duy.ide.code_sample.adapters.ProjectCategoryAdapter;
import com.duy.ide.code_sample.model.CodeCategory;
import com.duy.ide.code_sample.model.CodeProjectSample;

/**
 * Created by Duy on 27-Jul-17.
 */

public class SelectProjectFragment extends Fragment {
    public static final String TAG = "SampleFragment";

    private ProjectClickListener listener;

    public static SelectProjectFragment newInstance(CodeCategory category) {

        Bundle args = new Bundle();
        args.putSerializable("category", category);
        SelectProjectFragment fragment = new SelectProjectFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_category, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final CodeCategory category = (CodeCategory) getArguments().getSerializable("category");
        final RecyclerView recyclerView = view.findViewById(R.id.list_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        ProjectCategoryAdapter adapter = new ProjectCategoryAdapter(getActivity(), category);
        adapter.setListener(listener);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (ProjectClickListener) getActivity();
        } catch (Exception e) {
            //class cast exception
        }
    }


    public interface ProjectClickListener extends SelectCategoryFragment.CategoryClickListener {
        void onProjectClick(CodeProjectSample codeProjectSample);
    }
}
<file_sep>/fileexplorer/src/main/java/com/jecelyin/android/file_explorer/util/FileUtils.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.android.file_explorer.util;

import com.jecelyin.android.file_explorer.ExplorerException;
import com.jecelyin.android.file_explorer.io.JecFile;
import com.jecelyin.android.file_explorer.listener.BoolResultListener;
import com.jecelyin.android.file_explorer.listener.FileListResultListener;
import com.stericson.RootTools.RootTools;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <NAME> <<EMAIL>>
 */
public class FileUtils {
    private static Boolean mRootAccess = null;

    public static boolean hasRootAccess() {
        if (mRootAccess != null)
            return mRootAccess;
        try {
            mRootAccess = RootTools.isAccessGiven();
        } catch (Exception e) {
            mRootAccess = false;
        }
        return mRootAccess;
    }


    /**
     * Indicates whether file is considered to be "text".
     *
     * @return {@code true} if file is text, {@code false} if not.
     */
    public static boolean isTextFile(File f) {
        return !f.isDirectory() && isMimeText(f.getPath());
    }

    /**
     * Indicates whether requested file path is "text". This is done by
     * comparing file extension to a static list of extensions known to be text.
     * If the file has no file extension, it is also considered to be text.
     *
     * @param file File path
     * @return {@code true} if file is text, {@code false} if not.
     */
    private static boolean isMimeText(String file) {
        if (file == null)
            return false;
        if (!file.contains("."))
            return false;
        file = file.substring(file.lastIndexOf("/") + 1);
//        String ext = file.substring(file.lastIndexOf(".") + 1);
        return MimeTypes.getInstance().getMimeType(file).startsWith("text/");
    }

    public static void copyDirectory(final JecFile srcDir, JecFile destDir
                                     , final boolean moveFile) {
        if (srcDir == null) {
            throw new NullPointerException("Source must not be null");
        }
        if (destDir == null) {
            throw new NullPointerException("Destination must not be null");
        }
        if (!srcDir.exists()) {
            throw new ExplorerException("Source '" + srcDir + "' does not exist");
        }
        if (!srcDir.isDirectory()) {
            throw new ExplorerException("Source '" + srcDir + "' exists but is not a directory");
        }
        if (srcDir.getAbsolutePath().equals(destDir.getAbsolutePath())) {
            throw new ExplorerException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
        }

        final JecFile destDir2 = destDir.newFile(srcDir.getName());

        // Cater for destination being directory within the source directory (see IO-141)
        if (destDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
            srcDir.listFiles(new FileListResultListener() {
                @Override
                public void onResult(JecFile[] srcFiles) {
                    List<JecFile> exclusionList = null;
                    if (srcFiles != null && srcFiles.length > 0) {
                        exclusionList = new ArrayList<>(srcFiles.length);
                        for (JecFile srcFile : srcFiles) {
                            JecFile copiedFile = destDir2.newFile(srcFile.getName());
                            exclusionList.add(copiedFile.getAbsoluteFile());
                        }
                    }
                    doCopyDirectory(srcDir, destDir2, moveFile, exclusionList);
                }
            });
        } else {
            doCopyDirectory(srcDir, destDir2, moveFile, null);
        }
    }

    private static void doCopyDirectory(final JecFile srcDir, final JecFile destDir,
                                        final boolean moveFile, final List<JecFile> exclusionList) {
        srcDir.listFiles(new FileListResultListener() {
            @Override
            public void onResult(final JecFile[] srcFiles) {
                if (srcFiles == null) {  // null if abstract pathname does not denote a directory, or if an I/O error occurs
                    throw new ExplorerException("Failed to list contents of " + srcDir);
                }
                if (destDir.exists()) {
                    if (!destDir.isDirectory()) {
                        throw new ExplorerException("Destination '" + destDir + "' exists but is not a directory");
                    }
                    doCopyDirectory(srcDir, destDir, srcFiles, moveFile, exclusionList);
                } else {
                    destDir.mkdirs(new BoolResultListener() {
                        @Override
                        public void onResult(boolean result) {
                            if (!result && !destDir.isDirectory()) {
                                throw new ExplorerException("Destination '" + destDir + "' directory cannot be created");
                            }
                            doCopyDirectory(srcDir, destDir, srcFiles, moveFile, exclusionList);
                        }
                    });
                }
            }
        });
    }

    private static void doCopyDirectory(JecFile srcDir, final JecFile destDir, JecFile[] srcFiles,
                                        final boolean moveFile, final List<JecFile> exclusionList) throws ExplorerException {
        if (!destDir.canWrite()) {
            throw new ExplorerException("Destination '" + destDir + "' cannot be written to");
        }
        for (final JecFile srcFile : srcFiles) {
            JecFile dstFile = destDir.newFile(srcFile.getName()); //new File(destDir, srcFile.getName());
            if (exclusionList == null || !exclusionList.contains(srcFile.getAbsoluteFile())) {
                if (srcFile.isDirectory()) {
                    doCopyDirectory(srcFile, dstFile, moveFile, exclusionList);
                } else {
                    copyFile(srcFile, dstFile, moveFile);
                }
            }
        }
    }

    public static void copyFile(final JecFile srcFile, final JecFile dstFile, boolean moveFile) {
        if (moveFile) {
            srcFile.renameTo(dstFile, new BoolResultListener() {
                @Override
                public void onResult(boolean result) {
                    if (!result)
                        throw new ExplorerException("Source '" + srcFile + "' move to destination '" + dstFile + "' fail");
                }
            });
        } else {
            srcFile.copyTo(dstFile, new BoolResultListener() {
                @Override
                public void onResult(boolean result) {
                    if (!result)
                        throw new ExplorerException("Source '" + srcFile + "' copy to destination '" + dstFile + "' fail");
                }
            });
        }
    }

}
<file_sep>/app/src/main/java/com/duy/ide/formatter/FormatFactory.java
package com.duy.ide.formatter;

import android.content.Context;

import com.android.annotations.Nullable;
import com.duy.ide.setting.AppSetting;
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import com.google.googlejavaformat.java.JavaFormatterOptions;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

/**
 * Created by Duy on 13-Aug-17.
 */

public class FormatFactory {
    @Nullable
    public static Type getType(File ext) {
        if (ext.getPath().contains(".")) {
            switch (ext.getPath().substring(ext.getPath().lastIndexOf(".") + 1).toLowerCase()) {
                case "java":
                    return Type.JAVA;
                case "xml":
                    return Type.XML;
                default:
                    return null;
            }
        } else {
            return null;
        }
    }

    public static String format(Context context, String src, @Nullable Type type) throws Exception {
        if (type == null) throw new UnsupportedTypeException();
        switch (type) {
            case JAVA:
                return formatJava(context, src);
            case XML:
                return formatXml(context, src);
            default:
                return src;
        }
    }

    private static String formatJava(Context context, String src) throws FormatterException {
        AppSetting setting = new AppSetting(context);
        JavaFormatterOptions.Builder builder = JavaFormatterOptions.builder();
        builder.style(setting.getFormatType() == 0
                ? JavaFormatterOptions.Style.GOOGLE : JavaFormatterOptions.Style.AOSP);
        return new Formatter(builder.build()).formatSource(src);
    }

    private static String formatXml(Context context, String src) throws TransformerException,
            ParserConfigurationException, IOException, SAXException {
        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = builder.parse(new InputSource(new StringReader(src)));

        AppSetting setting = new AppSetting(context);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", setting.getTab().length() + "");
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

        //initialize StreamResult with File object to save to file
        StreamResult result = new StreamResult(new StringWriter());
        DOMSource source = new DOMSource(doc);
        transformer.transform(source, result);
        return result.getWriter().toString();
    }

    public enum Type {XML, JAVA}
}
<file_sep>/app/src/main/java/com/duy/run/view/ConsoleEditText.java
package com.duy.run.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;

import com.duy.ide.setting.AppSetting;
import com.duy.run.utils.IntegerQueue;
import com.spartacusrex.spartacuside.util.ByteQueue;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Created by Duy on 30-Jul-17.
 */

public class ConsoleEditText extends AppCompatEditText {
    private static final String TAG = "ConsoleEditText";
    private static final int NEW_OUTPUT = 1;
    private static final int NEW_ERR = 2;


    //length of text
    private int mLength = 0;

    //out, in and err stream
    private  PrintStream outputStream;
    private  InputStream inputStream;
    private  PrintStream errorStream;

    /**
     * uses for input
     */
    private IntegerQueue mInputBuffer = new IntegerQueue(IntegerQueue.QUEUE_SIZE);

    /**
     * buffer for output
     */
    private ByteQueue mStdoutBuffer = new ByteQueue(IntegerQueue.QUEUE_SIZE);

    /**
     * buffer for output
     */
    private ByteQueue mStderrBuffer = new ByteQueue(IntegerQueue.QUEUE_SIZE);
    private AtomicBoolean isRunning = new AtomicBoolean(true);

    //filter input text, block a part of text
    private TextListener mTextListener = new TextListener();
    private EnterListener mEnterListener = new EnterListener();
    private byte[] mReceiveBuffer;
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (!isRunning.get()) {
                return;
            }
            if (msg.what == NEW_OUTPUT) {
                writeStdoutToScreen();
            } else if (msg.what == NEW_ERR) {
                writeStderrToScreen();
            }
        }
    };

    public ConsoleEditText(Context context) {
        super(context);
        init(context);
    }

    public ConsoleEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ConsoleEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        if (!isInEditMode()) {
            AppSetting pref = new AppSetting(context);
            setTypeface(pref.getConsoleFont());
            setTextSize(pref.getConsoleTextSize());
        }
        setFilters(new InputFilter[]{mTextListener});
        setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
        addTextChangedListener(mEnterListener);
        setMaxLines(2000);

        createIOStream();
    }

    private void createIOStream() {
        mReceiveBuffer = new byte[4 * 1024];
        mStdoutBuffer = new ByteQueue(4 * 1024);
        mStderrBuffer = new ByteQueue(4 * 1024);

        inputStream = new ConsoleInputStream(mInputBuffer);
        outputStream = new PrintStream(new ConsoleOutputStream(mStdoutBuffer, new StdListener() {
            @Override
            public void onUpdate() {
                mHandler.sendMessage(mHandler.obtainMessage(NEW_OUTPUT));
            }
        }));
        errorStream = new PrintStream(new ConsoleErrorStream(mStderrBuffer, new StdListener() {
            @Override
            public void onUpdate() {
                mHandler.sendMessage(mHandler.obtainMessage(NEW_ERR));
            }
        }));
    }

    private void writeStdoutToScreen() {

        int bytesAvailable = mStdoutBuffer.getBytesAvailable();
        int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
        try {
            int bytesRead = mStdoutBuffer.read(mReceiveBuffer, 0, bytesToRead);
//                        mEmulator.append(mReceiveBuffer, 0, bytesRead);
            String out = new String(mReceiveBuffer, 0, bytesRead);
            mLength = mLength + out.length();
            appendStdout(out);
        } catch (InterruptedException e) {
        }
    }

    private void writeStderrToScreen() {

        int bytesAvailable = mStderrBuffer.getBytesAvailable();
        int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
        try {
            int bytesRead = mStderrBuffer.read(mReceiveBuffer, 0, bytesToRead);
//                        mEmulator.append(mReceiveBuffer, 0, bytesRead);
            String out = new String(mReceiveBuffer, 0, bytesRead);
            mLength = mLength + out.length();
            appendStderr(out);
        } catch (InterruptedException e) {
        }
//
//        String out = new String(Character.toChars(read));
//        mLength = mLength + out.length();
//        appendStdout(out);
    }

    @WorkerThread
    public PrintStream getOutputStream() {
        return outputStream;
    }

    @WorkerThread
    public InputStream getInputStream() {
        return inputStream;
    }

    @WorkerThread
    public PrintStream getErrorStream() {
        return errorStream;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @UiThread
    private void appendStdout(final CharSequence spannableString) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                append(spannableString);
            }
        });
    }

    @UiThread
    private void appendStderr(final CharSequence str) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                SpannableString spannableString = new SpannableString(str);
                spannableString.setSpan(new ForegroundColorSpan(Color.MAGENTA), 0, str.length(),
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                append(spannableString);
            }
        });
    }


    public void stop() {
        mInputBuffer.write(-1);
        isRunning.set(false);
    }

    public interface StdListener {
        void onUpdate();
    }

    private static class ConsoleOutputStream extends OutputStream {
        private ByteQueue mStdoutBuffer;
        private StdListener listener;

        private ConsoleOutputStream(ByteQueue mStdoutBuffer, StdListener listener) {
            this.mStdoutBuffer = mStdoutBuffer;
            this.listener = listener;
        }

        @Override
        public void write(@NonNull byte[] b, int off, int len) throws IOException {
            try {
                mStdoutBuffer.write(b, off, len);
                listener.onUpdate();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void write(int b) throws IOException {
            write(new byte[]{(byte) b}, 0, 1);
        }
    }

    private static class ConsoleErrorStream extends OutputStream {
        private ByteQueue mStderrBuffer;
        private StdListener stdListener;

        public ConsoleErrorStream(ByteQueue mStderrBuffer, StdListener stdListener) {
            this.mStderrBuffer = mStderrBuffer;
            this.stdListener = stdListener;
        }

        @Override
        public void write(@NonNull byte[] b, int off, int len) throws IOException {
            try {
                mStderrBuffer.write(b, off, len);
                stdListener.onUpdate();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void write(int b) throws IOException {
            write(new byte[]{(byte) b}, 0, 1);
        }
    }

    private static class ConsoleInputStream extends InputStream {
        private final Object mLock = new Object();
        @NonNull
        private IntegerQueue mInputBuffer;

        public ConsoleInputStream(@NonNull IntegerQueue mInputBuffer) {
            this.mInputBuffer = mInputBuffer;
        }

        @Override
        public int read() throws IOException {
            synchronized (mLock) {
                return mInputBuffer.read();
            }
        }
    }

    private class EnterListener implements TextWatcher {

        private int start;
        private int count;

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            this.start = start;
            this.count = count;
        }

        @Override
        public void afterTextChanged(Editable s) {
            if (count == 1 && s.charAt(start) == '\n' && start >= mLength) {
                String data = s.toString().substring(mLength);
                for (char c : data.toCharArray()) {
                    mInputBuffer.write(c);
                }
                mInputBuffer.write(-1); //flush
                mLength = s.length(); //append to console
            }
        }
    }

    private class TextListener implements InputFilter {
        public CharSequence removeStr(CharSequence removeChars, int startPos) {
            if (startPos < mLength) { //this mean output from console
                return removeChars; //can not remove console output
            } else {
                return "";
            }
        }

        public CharSequence insertStr(CharSequence newChars, int startPos) {
            if (startPos < mLength) { //it mean output from console
                return newChars;

            } else { //(startPos >= mLength)
                SpannableString spannableString = new SpannableString(newChars);
                spannableString.setSpan(new ForegroundColorSpan(Color.GREEN), 0,
                        spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                return spannableString;

            }
        }

        public CharSequence updateStr(CharSequence oldChars, int startPos, CharSequence newChars) {
            if (startPos < mLength) {
                return oldChars; //don't edit

            } else {//if (startPos >= mLength)
                SpannableString spannableString = new SpannableString(newChars);
                spannableString.setSpan(new ForegroundColorSpan(Color.GREEN), 0,
                        spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                return spannableString;
            }
        }

        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            CharSequence returnStr = source;
            String curStr = dest.subSequence(dstart, dend).toString();
            String newStr = source.toString();
            int length = end - start;
            int dlength = dend - dstart;
            if (dlength > 0 && length == 0) {
                // Case: Remove chars, Simple
                returnStr = TextListener.this.removeStr(dest.subSequence(dstart, dend), dstart);
            } else if (length > 0 && dlength == 0) {
                // Case: Insert chars, Simple
                returnStr = TextListener.this.insertStr(source.subSequence(start, end), dstart);
            } else if (curStr.length() > newStr.length()) {
                // Case: Remove string or replace
                if (curStr.startsWith(newStr)) {
                    // Case: Insert chars, by append
                    returnStr = TextUtils.concat(curStr.subSequence(0, newStr.length()), TextListener.this.removeStr(curStr.subSequence(newStr.length(), curStr.length()), dstart + curStr.length()));
                } else {
                    // Case Replace chars.
                    returnStr = TextListener.this.updateStr(curStr, dstart, newStr);
                }
            } else if (curStr.length() < newStr.length()) {
                // Case: Append String or rrepace.
                if (newStr.startsWith(curStr)) {
                    // Addend, Insert
                    returnStr = TextUtils.concat(curStr, TextListener.this.insertStr(newStr.subSequence(curStr.length(), newStr.length()), dstart + curStr.length()));
                } else {
                    returnStr = TextListener.this.updateStr(curStr, dstart, newStr);
                }
            } else {
                // No update os str...
            }

            // If the return value is same as the source values, return the source value.
            return returnStr;
        }
    }
}
<file_sep>/app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    useLibrary 'org.apache.http.legacy'

    defaultConfig {
        applicationId "com.duy.compiler.javanide"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        multiDexEnabled true
        versionCode 33
        versionName "1.2.5"
        vectorDrawables.useSupportLibrary = true

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            ndk {
                moduleName "aapt"
                abiFilters 'armeabi-v7a', 'x86'
            }
        }
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            ndk {
                moduleName "aapt"
                abiFilters 'armeabi-v7a'
            }
        }
    }

    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
    }

    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }

    dataBinding {
        enabled = true
    }

    lintOptions {
        disable 'MissingTranslation'
    }

}


configurations {
    all*.exclude group: 'commons-logging', module: 'commons-logging'
}


repositories {
    mavenCentral()
}
dependencies {
    testCompile 'junit:junit:4.12'
    compile fileTree(dir: 'libs', include: ['*.jar'])

//    compile files('libs/commons-codec-1.10.jar')
//    compile files('libs/commons-compress-1.5.jar')
//    compile files('libs/dx.jar')

    compile project(':pager')
    compile project(':colorpicker')
    compile project(':splitview')
    compile project(':treeview')
    compile project(':fileexplorer')
    compile project(':javacompiler')
    compile project(':dx')
    compile project(':javacodeformatter')
    compile project(':androidlogcat')
    compile project(':manifest_merger')

    //android support
    compile 'com.android.support:multidex:1.0.2'
    compile "com.android.support:support-annotations:$supportLibVersion"
    compile "com.android.support:support-v4:$supportLibVersion"
    compile "com.android.support:appcompat-v7:$supportLibVersion"
    compile "com.android.support:design:$supportLibVersion"
    compile "com.android.support:cardview-v7:$supportLibVersion"
    compile "com.android.support:recyclerview-v7:$supportLibVersion"

    //firebase sdk
    compile "com.google.firebase:firebase-core:$googleServiceVersion"
    compile('com.crashlytics.sdk.android:crashlytics:2.7.1@aar') {
        transitive = true
    }

    compile 'io.github.kobakei:ratethisapp:1.2.0'
    compile 'commons-io:commons-io:2.4'
    compile 'com.github.clans:fab:1.6.4'
    compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
    compile 'com.miguelcatalan:materialsearchview:1.4.0'
    compile 'com.sothree.slidinguppanel:library:3.3.1'
}

apply plugin: 'com.google.gms.google-services'
<file_sep>/styles/src/main/java/com/jecelyin/editor/v2/Pref.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.editor.v2;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.annotation.IntDef;
import android.text.TextUtils;

import com.jecelyin.common.utils.L;
import com.jecelyin.common.utils.StringUtils;
import com.jecelyin.common.utils.SysUtils;
import com.jecelyin.styles.R;
import com.stericson.RootTools.RootTools;

import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

/**
 * @author <NAME> <<EMAIL>>
 */
public class Pref implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String KEY_FONT_SIZE = "pref_font_size";
    public static final String KEY_CURSOR_WIDTH = "pref_cursor_width";
    public static final String KEY_TOUCH_TO_ADJUST_TEXT_SIZE = "pref_touch_to_adjust_text_size";
    public static final String KEY_WORD_WRAP = "pref_word_wrap";
    public static final String KEY_SHOW_LINE_NUMBER = "pref_show_linenumber";
    public static final String KEY_SHOW_WHITESPACE = "pref_show_whitespace";
    public static final String KEY_AUTO_INDENT = "pref_auto_indent";
    public static final String KEY_INSERT_SPACE_FOR_TAB = "pref_insert_space_for_tab";
    public static final String KEY_TAB_SIZE = "pref_tab_size";
    public static final String KEY_SYMBOL = "pref_symbol";
    public static final String KEY_AUTO_CAPITALIZE = "pref_auto_capitalize";
    public static final String KEY_ENABLE_HIGHLIGHT = "pref_enable_highlight";
    public static final String KEY_HIGHLIGHT_FILE_SIZE_LIMIT = "pref_highlight_file_size_limit";
    public static final String KEY_THEME = "pref_current_theme";
    public static final String KEY_AUTO_SAVE = "pref_auto_save";
    public static final String KEY_REMEMBER_LAST_OPENED_FILES = "pref_remember_last_opened_files";
    public static final String KEY_SCREEN_ORIENTATION = "pref_screen_orientation";
    public static final String KEY_KEEP_SCREEN_ON = "pref_keep_screen_on";
    public static final String KEY_ENABLE_ROOT = "pref_enable_root";
    public static final String KEY_TOOLBAR_ICONS = "pref_toolbar_icons";
    public static final String KEY_PREF_AUTO_CHECK_UPDATES = "pref_auto_check_updates";
    public static final String KEY_PREF_KEEP_BACKUP_FILE = "pref_keep_backup_file";
    public static final String KEY_PREF_ENABLE_DRAWERS = "pref_enable_drawers";
    public static final String KEY_LAST_OPEN_PATH = "last_open_path";
    public static final String KEY_READ_ONLY = "readonly_mode";
    public static final String KEY_SHOW_HIDDEN_FILES = "show_hidden_files";
    public static final String KEY_FILE_SORT_TYPE = "show_file_sort";
    public static final String KEY_FULL_SCREEN = "fullscreen_mode";
    public static final String KEY_LAST_TAB = "last_tab";

    public static final int DEF_MIN_FONT_SIZE = 9;
    public static final int DEF_MAX_FONT_SIZE = 32;

    public static final int SCREEN_ORIENTATION_AUTO = 0;
    public static final int SCREEN_ORIENTATION_LANDSCAPE = 1;
    public static final int SCREEN_ORIENTATION_PORTRAIT = 2;
    public static final String VALUE_SYMBOL = TextUtils.join("\n", new String[]{"{", "}", "<", ">"
            , ",", ";", "'", "\"", "(", ")", "/", "\\", "%", "[", "]", "|", "#", "=", "$", ":"
            , "&", "?", "!", "@", "^", "+", "*", "-", "_", "`", "\\t", "\\n"});

    public static final int[] THEMES = new int[]{
            R.style.DefaultTheme,
            R.style.DarkTheme
    };
    private static final Object mContent = new Object();
    private static Pref instance;

    static {
        // All Private Keys should go here like this:
//        privateKeys.put("box_key", "<KEY>");
//        privateKeys.put("box_secret", "<KEY>");
//        privateKeys.put("dropbox_key", "<KEY>");
//        privateKeys.put("dropbox_secret", "plkrfrygu17glgn");
//        privateKeys.put("drive_key", "645291897772.apps.googleusercontent.com");
//        privateKeys.put("drive_secret", "<KEY>");
//        privateKeys.put("skydrive_key", "00000000400F4500");
//        privateKeys.put("skydrive_secret", "<KEY>");
    }

    private final SharedPreferences pm;
    private final Map<String, Object> map;
    private final Context context;
    private final WeakHashMap<SharedPreferences.OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<>();
    private Set<String> toolbarIcons;

    public Pref(Context context) {
        this.context = context;
        pm = PreferenceManager.getDefaultSharedPreferences(context);
        pm.registerOnSharedPreferenceChangeListener(this);

        //init variable
        map = new HashMap<>();
        map.put(KEY_FONT_SIZE, 13);
        map.put(KEY_CURSOR_WIDTH, 2);
        map.put(KEY_TOUCH_TO_ADJUST_TEXT_SIZE, false);
        map.put(KEY_WORD_WRAP, true);
        map.put(KEY_SHOW_LINE_NUMBER, true);
        map.put(KEY_SHOW_WHITESPACE, true);
        map.put(KEY_AUTO_INDENT, true);
        map.put(KEY_INSERT_SPACE_FOR_TAB, true);
        map.put(KEY_TAB_SIZE, 4);
        map.put(KEY_SYMBOL, VALUE_SYMBOL);
        map.put(KEY_AUTO_CAPITALIZE, true);
        map.put(KEY_ENABLE_HIGHLIGHT, true);
        map.put(KEY_HIGHLIGHT_FILE_SIZE_LIMIT, 500);
        map.put(KEY_THEME, 0);
        map.put(KEY_AUTO_SAVE, false);
        map.put(KEY_ENABLE_ROOT, true);
        map.put(KEY_REMEMBER_LAST_OPENED_FILES, true);
        map.put(KEY_SCREEN_ORIENTATION, "auto");
        map.put(KEY_KEEP_SCREEN_ON, false);
        map.put(KEY_PREF_AUTO_CHECK_UPDATES, true);
        map.put(KEY_PREF_KEEP_BACKUP_FILE, true);
        map.put(KEY_PREF_ENABLE_DRAWERS, true);

        //not at preference setting
        toolbarIcons = pm.getStringSet(KEY_TOOLBAR_ICONS, null);
        map.put(KEY_LAST_OPEN_PATH, Environment.getExternalStorageDirectory().getPath() +
                File.separator + "JavaNIDE");
        map.put(KEY_READ_ONLY, false);
        map.put(KEY_SHOW_HIDDEN_FILES, false);
        map.put(KEY_FILE_SORT_TYPE, 0);
        map.put(KEY_FULL_SCREEN, false);
        map.put(KEY_LAST_TAB, 0);

        Map<String, ?> values = pm.getAll();
        for (String key : map.keySet()) {
            updateValue(key, values);
        }
    }

    public static Pref getInstance(Context context) {
        if (instance == null) {
            instance = new Pref(context.getApplicationContext());
        }
        return instance;
    }

    public static String getGoogleDriveKey() {
        return null; //drive_key
    }

    public static String getGoogleDriveSecret() {
        return null; //drive_key
    }

    public static String getBoxAPIKey() {
        return null; // TODO: 16/1/2
    }

    public static String getBoxApiSecret() {
        return null;
    }

    private void updateValue(String key, Map<String, ?> values) {
        Object value = map.get(key);
        // 跳过一些不能通过本方法取值的东东
        if (value == null)
            return;
        Class cls = value.getClass();

        try {
            if (cls == int.class || cls == Integer.class) {
//                value = StringUtils.toInt(pm.getString(key, String.valueOf(value)));
                Object in = values.get(key);
                if (in != null)
                    value = in instanceof Integer ? (int) in : StringUtils.toInt(String.valueOf(in));
            } else if (cls == boolean.class || cls == Boolean.class) {
//                value = pm.getBoolean(key, (boolean)value);
                Boolean b = (Boolean) values.get(key);
                value = b == null ? (boolean) value : b;
            } else {
//                value = pm.getString(key, (String)value);
                String str = (String) values.get(key);
                value = str == null ? (String) value : str;
            }
        } catch (Exception e) {
            L.e("key = " + key, e);
            return;
        }
        map.put(key, value);
    }

    public void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
        synchronized (this) {
            mListeners.put(listener, mContent);
        }
    }

    public void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
        synchronized (this) {
            mListeners.remove(listener);
        }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        updateValue(key, sharedPreferences.getAll());
        Set<SharedPreferences.OnSharedPreferenceChangeListener> listeners = mListeners.keySet();
        for (SharedPreferences.OnSharedPreferenceChangeListener listener : listeners) {
            if (listener != null) {
                listener.onSharedPreferenceChanged(sharedPreferences, key);
            }
        }
    }

    public boolean isShowLineNumber() {
        return (boolean) map.get(KEY_SHOW_LINE_NUMBER);
    }

    public boolean isShowWhiteSpace() {
        return (boolean) map.get(KEY_SHOW_WHITESPACE);
    }

    public int getTheme() {
        return (int) map.get(KEY_THEME);
    }

    /**
     * theme index of {@link #THEMES}
     *
     * @param theme
     */
    public void setTheme(int theme) {
        map.put(KEY_THEME, theme);
        pm.edit().putInt(KEY_THEME, theme).commit();
    }

    public boolean isHighlight() {
        return (boolean) map.get(KEY_ENABLE_HIGHLIGHT);
    }

    public int getHighlightSizeLimit() {
        return 1024 * (int) map.get(KEY_HIGHLIGHT_FILE_SIZE_LIMIT);
    }

    public boolean isAutoSave() {
        return (boolean) map.get(KEY_AUTO_SAVE);
    }

    public boolean isKeepScreenOn() {
        return (boolean) map.get(KEY_KEEP_SCREEN_ON);
    }

    public Integer[] getToolbarIcons() {
        if (toolbarIcons == null)
            return null;
        Integer[] list = new Integer[toolbarIcons.size()];
        int i = 0;
        for (String id : toolbarIcons) {
            list[i++] = Integer.valueOf(id);
        }
        return list;
    }

    public void setToolbarIcons(Integer[] toolbarIcons) {
        this.toolbarIcons = new HashSet<>();
        for (Integer id : toolbarIcons) {
            this.toolbarIcons.add(String.valueOf(id));
        }
        pm.edit().putStringSet(KEY_TOOLBAR_ICONS, this.toolbarIcons).apply();
    }

    public Object getValue(String key) {
        return map.get(key);
    }

    public String getLastOpenPath() {
        return (String) map.get(KEY_LAST_OPEN_PATH);
    }

    public void setLastOpenPath(String path) {
        pm.edit().putString(KEY_LAST_OPEN_PATH, path).apply();
        map.put(KEY_LAST_OPEN_PATH, path);
    }

    public int getFontSize() {
        return (int) map.get(KEY_FONT_SIZE);
    }

    public int getCursorThickness() {
        int width = (int) map.get(KEY_CURSOR_WIDTH);
        if (width == 0)
            return 0;

        return SysUtils.dpAsPixels(context, width);
    }

    public boolean isReadOnly() {
        return (boolean) map.get(KEY_READ_ONLY);
    }

    public void setReadOnly(boolean b) {
        pm.edit().putBoolean(KEY_READ_ONLY, b).apply();
        map.put(KEY_READ_ONLY, b);
    }

    public boolean isAutoIndent() {
        return (boolean) map.get(KEY_AUTO_INDENT);
    }

    public boolean isWordWrap() {
        return (boolean) map.get(KEY_WORD_WRAP);
    }

    public boolean isTouchScaleTextSize() {
        return (boolean) map.get(KEY_TOUCH_TO_ADJUST_TEXT_SIZE);
    }

    public boolean isAutoCheckUpdates() {
        return (boolean) map.get(KEY_PREF_AUTO_CHECK_UPDATES);
    }

    public boolean isAutoCapitalize() {
        return (boolean) map.get(KEY_AUTO_CAPITALIZE);
    }

    public boolean isOpenLastFiles() {
        return (boolean) map.get(KEY_REMEMBER_LAST_OPENED_FILES);
    }

    public int getTabSize() {
        return (int) map.get(KEY_TAB_SIZE);
    }

    @ScreenOrientation
    public int getScreenOrientation() {
        String ori = (String) map.get(KEY_SCREEN_ORIENTATION);
        if ("landscape".equals(ori)) {
            return SCREEN_ORIENTATION_LANDSCAPE;
        } else if ("portrait".equals(ori)) {
            return SCREEN_ORIENTATION_PORTRAIT;
        } else {
            return SCREEN_ORIENTATION_AUTO;
        }
    }

    public boolean isRootable() {
        return ((boolean) map.get(KEY_ENABLE_ROOT)) && RootTools.isRootAvailable() && RootTools.isAccessGiven();
    }

    public boolean isKeepBackupFile() {
        return (boolean) map.get(KEY_PREF_KEEP_BACKUP_FILE);
    }

    public String getSymbol() {
        return (String) map.get(KEY_SYMBOL);
    }

    public boolean isShowHiddenFiles() {
        return (boolean) map.get(KEY_SHOW_HIDDEN_FILES);
    }

    public void setShowHiddenFiles(boolean b) {
        pm.edit().putBoolean(KEY_SHOW_HIDDEN_FILES, b).apply();
        map.put(KEY_SHOW_HIDDEN_FILES, b);
    }

    public int getFileSortType() {
        return (int) map.get(KEY_FILE_SORT_TYPE);
    }

    public void setFileSortType(int type) {
        pm.edit().putInt(KEY_FILE_SORT_TYPE, type).apply();
        map.put(KEY_FILE_SORT_TYPE, type);
    }

    public boolean isFullScreenMode() {
        return (boolean) map.get(KEY_FULL_SCREEN);
    }

    public void setFullScreenMode(boolean b) {
        pm.edit().putBoolean(KEY_FULL_SCREEN, b).apply();
        map.put(KEY_FULL_SCREEN, b);
    }

    public int getLastTab() {
        return (int) map.get(KEY_LAST_TAB);
    }

    public void setLastTab(int index) {
        pm.edit().putInt(KEY_LAST_TAB, index).apply();
        map.put(KEY_LAST_TAB, index);
    }

    public boolean isEnabledDrawers() {
        return (boolean) map.get(KEY_PREF_ENABLE_DRAWERS);
    }

    @IntDef({SCREEN_ORIENTATION_AUTO, SCREEN_ORIENTATION_LANDSCAPE, SCREEN_ORIENTATION_PORTRAIT})
    public @interface ScreenOrientation {
    }
}
<file_sep>/app/src/main/java/com/duy/ide/editor/code/highlight/xml/XmlHighlighter.java
package com.duy.ide.editor.code.highlight.xml;

import android.support.annotation.NonNull;
import android.support.v4.util.Pair;
import android.text.Editable;
import android.text.Spannable;
import android.text.style.ForegroundColorSpan;

import com.duy.ide.editor.code.highlight.HighlightImpl;
import com.duy.ide.editor.code.highlight.java.StringHighlighter;
import com.duy.ide.editor.code.view.HighlightEditor;
import com.duy.ide.themefont.themes.database.CodeTheme;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.regex.Matcher;

import static com.duy.ide.autocomplete.Patterns.XML_ATTRS;
import static com.duy.ide.autocomplete.Patterns.XML_TAGS;

/**
 * Created by Duy on 06-Aug-17.
 */

public class XmlHighlighter extends HighlightImpl {
    private StringHighlighter stringHighlighter;
    private XmlCommentHighlighter commentHighlighter;

    public XmlHighlighter(HighlightEditor highlightEditor) {
        this.codeTheme = highlightEditor.getCodeTheme();
        this.commentHighlighter = new XmlCommentHighlighter(codeTheme);
        this.stringHighlighter = new StringHighlighter(codeTheme);
    }

    @Override
    public void highlight(@NonNull Editable allText, @NonNull CharSequence textToHighlight, int start) {
        try {
            commentHighlighter.highlight(allText, textToHighlight, start);
            ArrayList<Pair<Integer, Integer>> region = commentHighlighter.getCommentRegion();
            if (region.size() == 0) {
                highlightStringAndOther(allText, textToHighlight, start);
                return;
            }
            Collections.sort(region, new Comparator<Pair<Integer, Integer>>() {
                @Override
                public int compare(Pair<Integer, Integer> o1, Pair<Integer, Integer> o2) {
                    return o1.first.compareTo(o2.first);
                }
            });

            int startIndex, endIndex;
            if (region.size() > 0 && start < region.get(0).first - 1) {
                highlightStringAndOther(allText,
                        allText.subSequence(start, region.get(0).first - 1), start);
            }
            for (int i = 0; i < region.size() - 1; i++) {
                startIndex = region.get(i).second + 1;
                endIndex = region.get(i + 1).first - 1;
                if (startIndex < endIndex) {
                    highlightStringAndOther(allText,
                            allText.subSequence(startIndex, endIndex), startIndex);
                }
            }
            if (region.size() > 0) {
                startIndex = region.get(region.size() - 1).second + 1;
                endIndex = start + textToHighlight.length();
                if (startIndex <= endIndex) {
                    highlightStringAndOther(allText,
                            allText.subSequence(startIndex, endIndex), startIndex);
                }
            }
        } catch (Exception ignored) {
            ignored.printStackTrace();
        }
    }

    @Override
    public void setCodeTheme(CodeTheme codeTheme) {
        super.setCodeTheme(codeTheme);
        commentHighlighter.setCodeTheme(codeTheme);
        stringHighlighter.setCodeTheme(codeTheme);
    }

    @Override
    public void setErrorRange(long startPosition, long endPosition) {

    }

    private void highlightStringAndOther(@NonNull Editable allText,
                                         @NonNull CharSequence textToHighlight, int start) {
        highlightAttr(allText, textToHighlight, start);
        stringHighlighter.highlight(allText, textToHighlight, start);
    }

    private void highlightAttr(@NonNull Editable allText,
                               @NonNull CharSequence textToHighlight, int start) {
        for (Matcher m = XML_ATTRS.matcher(textToHighlight); m.find(); ) {
            allText.setSpan(new ForegroundColorSpan(codeTheme.getNumberColor()),
                    start + m.start(),
                    start + m.end(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        for (Matcher m = XML_TAGS.matcher(textToHighlight); m.find(); ) {
            allText.setSpan(new ForegroundColorSpan(codeTheme.getKeywordColor()),
                    start + m.start(),
                    start + m.end(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }

}
<file_sep>/app/src/androidTest/java/com/duy/compile/external/java/JavaCompilerAPITest.java
package com.duy.compile.external.java;


import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.IOException;

/**
 * Created by duy on 19/07/2017.
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class JavaCompilerAPITest {

    @Before
    public void setup() {

    }

    @Test
    public void testCompile() throws IOException {

    }
}<file_sep>/app/src/main/java/com/duy/compile/external/CompileHelper.java
package com.duy.compile.external;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.duy.compile.external.android.AndroidBuilder;
import com.duy.compile.external.android.util.Util;
import com.duy.compile.external.dex.DexTool;
import com.duy.compile.external.java.Jar;
import com.duy.compile.external.java.Java;
import com.duy.compile.external.java.Javac;
import com.duy.dex.Dex;
import com.duy.dx.merge.CollisionPolicy;
import com.duy.dx.merge.DexMerger;
import com.duy.ide.DLog;
import com.duy.ide.file.FileManager;
import com.duy.project.file.android.AndroidProjectFolder;
import com.duy.project.file.java.JavaProjectFolder;
import com.sun.tools.javac.main.Main;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import javax.tools.DiagnosticCollector;
import javax.tools.DiagnosticListener;

/**
 * Created by duy on 18/07/2017.
 */

public class CompileHelper {
    private static final String TAG = "CommandManager";

    @Nullable
    public static File buildJarAchieve(Context context, JavaProjectFolder projectFile,
                                       DiagnosticListener listener) throws IOException {
        int status = compileJava(context, projectFile, listener);
        if (status != Main.EXIT_OK) {
            throw new RuntimeException("Compile time error... Exit code(" + status + ")");
        }
        //now create normal jar file
        Jar.createJarArchive(projectFile);
        return projectFile.getOutJarArchive();
    }

    public static int compileJava(Context context, JavaProjectFolder pf) {
        return compileJava(context, pf, null);
    }

    public static int compileJava(Context context, JavaProjectFolder projectFile, @Nullable DiagnosticListener listener) {
        try {

            String[] args = new String[]{
                    "-verbose",
                    "-cp", projectFile.getJavaClassPath(context),
                    "-sourcepath", projectFile.getSourcePath(), //sourcepath
                    "-d", projectFile.getDirBuildClasses().getPath(), //output dir
                    projectFile.getMainClass().getPath(projectFile) //main class
            };
            DLog.d(TAG, "compileJava args = " + Arrays.toString(args));
            int compileStatus;
            compileStatus = Javac.compile(args, listener);
            return compileStatus;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return Main.EXIT_ERROR;
    }

    public static void compileAndRun(Context context, InputStream in, File tempDir, JavaProjectFolder projectFile) throws Exception {
        compileJava(context, projectFile);
        convertToDexFormat(context, projectFile);
        executeDex(context, in, projectFile.getDexedClassesFile(), tempDir, projectFile.getMainClass().getName());
    }

    public static void dexLibs(@NonNull JavaProjectFolder projectFile) throws Exception {
        DLog.d(TAG, "dexLibs() called with: projectFile = [" + projectFile + "]");
        File dirLibs = projectFile.dirLibs;
        if (dirLibs.exists()) {
            File[] files = dirLibs.listFiles();
            if (files != null && files.length > 0) {
                for (File jarLib : files) {
                    // skip native libs in sub directories
                    if (!jarLib.isFile() || !jarLib.getName().endsWith(".jar")) {
                        continue;
                    }

                    // compare hash of jar contents to name of dexed version
                    String md5 = Util.getMD5Checksum(jarLib);

                    File dexLib = new File(projectFile.getDirDexedLibs(), jarLib.getName().replace(".jar", "-" + md5 + ".dex"));
                    if (dexLib.exists()) {
                        continue;
                    }
                    String[] args = new String[]{"--dex", "--verbose", "--no-strict",
                            "--output=" + dexLib.getPath(), jarLib.getPath()};
                    DexTool.main(args);
                }
            }
        }
    }

    public static File dexBuildClasses(@NonNull JavaProjectFolder projectFile) throws IOException {
        DLog.d(TAG, "dexBuildClasses() called with: projectFile = [" + projectFile + "]");
        String input = projectFile.dirBuildClasses.getPath();
        FileManager.ensureFileExist(new File(input));
        String[] args = new String[]{"--dex", "--verbose", "--no-strict",
                "--output=" + projectFile.getDexedClassesFile().getPath(), //output dex file
                input}; //input file
        DexTool.main(args);
        return projectFile.getDexedClassesFile();
    }

    public static File dexMerge(@NonNull JavaProjectFolder projectFile) throws IOException {
        DLog.d(TAG, "dexMerge() called with: projectFile = [" + projectFile + "]");
        FileManager.ensureFileExist(projectFile.getDexedClassesFile());

        if (projectFile.getDirDexedLibs().exists()) {
            File[] files = projectFile.getDirDexedLibs().listFiles();
            if (files != null && files.length > 0) {
                for (File dexedLib : files) {
                    DexMerger dexMerger = new DexMerger(
                            new Dex[]{
                                    new Dex(projectFile.getDexedClassesFile()),
                                    new Dex(dexedLib)},
                            CollisionPolicy.FAIL);
                    Dex merged = dexMerger.merge();
                    merged.writeTo(projectFile.getDexedClassesFile());
                }
            }
        }
        return projectFile.getDexedClassesFile();
    }

    public static void executeDex(Context context, InputStream in, @NonNull File outDex, @NonNull File tempDir, String mainClass)
            throws FileNotFoundException {
        FileManager.ensureFileExist(outDex);

        String[] args = new String[]{"-jar", outDex.getPath(), mainClass};
        Java.run(args, tempDir.getPath(), in);
    }

    public static void convertToDexFormat(Context context, @NonNull JavaProjectFolder projectFile) throws Exception {
        Log.d(TAG, "convertToDexFormat() called with: projectFile = [" + projectFile + "]");
        dexLibs(projectFile);
        dexBuildClasses(projectFile);
        dexMerge(projectFile);
    }

    public static File buildApk(Context context, AndroidProjectFolder projectFile,
                                DiagnosticCollector diagnosticCollector) throws Exception {
        AndroidBuilder.build(context, projectFile, diagnosticCollector);
        return projectFile.getApkUnaligned();
    }

    public class Action {
        public static final int RUN = 0;
        public static final int RUN_DEX = 1;
        public static final int BUILD_JAR = 2;
    }
}
<file_sep>/app/src/main/java/com/duy/project/view/dialog/DialogNewAndroidProject.java
package com.duy.project.view.dialog;

import android.app.Dialog;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatDialogFragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import com.duy.ide.R;
import com.duy.ide.autocomplete.Patterns;
import com.duy.ide.code_sample.model.AssetUtil;
import com.duy.ide.file.FileManager;
import com.duy.project.file.android.AndroidProjectFolder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by Duy on 16-Jul-17.
 */

public class DialogNewAndroidProject extends AppCompatDialogFragment implements View.OnClickListener {
    public static final String TAG = "DialogNewAndroidProject";
    private EditText editAppName, editPackage;
    private Button btnCreate, btnCancel;
    @Nullable
    private DialogNewJavaProject.OnCreateProjectListener listener;
    private EditText mActivityName, layoutName;
    private CheckBox mAppCompat;

    public static DialogNewAndroidProject newInstance() {

        Bundle args = new Bundle();

        DialogNewAndroidProject fragment = new DialogNewAndroidProject();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (DialogNewJavaProject.OnCreateProjectListener) getActivity();
        } catch (Exception e) {
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null) {
            dialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_new_android_project, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        editPackage = view.findViewById(R.id.edit_package_name);
        editAppName = view.findViewById(R.id.edit_project_name);
        btnCreate = view.findViewById(R.id.btn_create);
        btnCancel = view.findViewById(R.id.btn_cancel);
        btnCreate.setOnClickListener(this);
        btnCancel.setOnClickListener(this);
        mActivityName = view.findViewById(R.id.edit_activity_name);
        layoutName = view.findViewById(R.id.edit_layout_name);
        mAppCompat = view.findViewById(R.id.backwards_compatibility);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_cancel:
                dismiss();
                break;
            case R.id.btn_create:
                doCreateProject();
                break;
        }
    }

    private void doCreateProject() {
        if (isOk()) {

            ///create new android project
            String packageName = editPackage.getText().toString();
            String activityName = mActivityName.getText().toString();
            String activityClass = String.format("%s.%s", packageName, activityName);
            String mainLayoutName = layoutName.getText().toString();
            String appName = editAppName.getText().toString();
            String classpath = FileManager.getClasspathFile(getContext()).getPath();
            String projectName = appName.replaceAll("\\s+", "");
            boolean useAppCompat = mAppCompat.isChecked();
            try {
                AndroidProjectFolder projectFile = new AndroidProjectFolder(
                        new File(FileManager.EXTERNAL_DIR), activityClass, packageName, projectName);
                //create directory
                projectFile.mkdirs();

                AssetManager assets = getContext().getAssets();

                copyResources(projectFile, useAppCompat, assets);
                createStringXml(projectFile, appName);
                copyKeyStore(projectFile, assets);
                createManifest(projectFile, activityClass, packageName, assets);
                createMainActivity(projectFile, activityClass, packageName, activityName,
                        appName, useAppCompat, assets);
                createMainXml(projectFile, mainLayoutName, assets);
                copyLibrary(projectFile, assets);
                if (listener != null) listener.onProjectCreated(projectFile);
                this.dismiss();
            } catch (Exception e) {
                e.printStackTrace();
                Toast.makeText(getContext(), "Can not create project. Error " + e.getMessage(),
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void copyResources(AndroidProjectFolder projectFile, boolean useAppCompat, AssetManager assets) throws FileNotFoundException {
        String resourcePath = projectFile.getDirRes().getPath();
        AssetUtil.copyAssetFolder(assets, "templates/src/main/res", resourcePath);
        File file = new File(resourcePath, "values/styles.xml");
        String content = FileManager.streamToString(new FileInputStream(file)).toString();
        content = content.replace("{APP_STYLE}", useAppCompat ? "Theme.AppCompat.Light" : "@android:style/Theme.Holo.Light");
        FileManager.saveFile(file, content);
    }

    private void copyLibrary(AndroidProjectFolder projectFile, AssetManager assets) {
        //copy android support library
        AssetUtil.copyAssetFolder(assets, "templates/libs", projectFile.dirLibs.getPath());
    }

    private void copyKeyStore(AndroidProjectFolder projectFile, AssetManager assets) throws IOException {

        //copy keystore
        File file = projectFile.getKeyStore().getFile();
        FileOutputStream out = new FileOutputStream(file);
        FileManager.copyStream(assets.open("templates/src/main/androiddebug.jks"), out);
        out.close();

    }

    private void createStringXml(AndroidProjectFolder projectFile, String appName) throws Exception {
        File stringxml = new File(projectFile.getDirRes(), "values/strings.xml");
        String strings = FileManager.streamToString(new FileInputStream(
                stringxml)).toString();
        strings = strings.replace("{APP_NAME}", appName);
        strings = strings.replace("{MAIN_ACTIVITY_NAME}", appName);
        Log.d(TAG, "doCreateProject strings = " + strings);
        FileManager.saveFile(stringxml, strings);

    }

    private void createManifest(AndroidProjectFolder projectFile, String activityClass, String packageName,
                                AssetManager assets) throws IOException {
        File manifest = projectFile.getXmlManifest();
        InputStream manifestTemplate = assets.open("templates/src/main/AndroidManifest.xml");
        String contentManifest = FileManager.streamToString(manifestTemplate).toString();
        contentManifest = contentManifest.replace("{PACKAGE}", packageName);
        contentManifest = contentManifest.replace("{MAIN_ACTIVITY}", activityClass);
        Log.d(TAG, "doCreateProject contentManifest = " + contentManifest);
        FileManager.saveFile(manifest, contentManifest);
    }

    private void createMainActivity(AndroidProjectFolder projectFile, String activityClass,
                                    String packageName, String activityName, String appName,
                                    boolean useAppCompat, AssetManager assets) throws IOException {
        File activityFile = FileManager.createFileIfNeed(new File(projectFile.dirJava,
                activityClass.replace(".", File.separator) + ".java"));
        String name = useAppCompat ? "templates/src/main/MainActivityAppCompat.java" : "templates/src/main/MainActivity.java";
        InputStream activityTemplate = assets.open(name);
        String contentClass = FileManager.streamToString(activityTemplate).toString();
        contentClass = contentClass.replace("{PACKAGE}", packageName);
        contentClass = contentClass.replace("{APP_NAME}", appName);
        contentClass = contentClass.replace("{ACTIVITY_NAME}", activityName);
        Log.d(TAG, "doCreateProject contentManifest = " + contentClass);
        FileManager.saveFile(activityFile, contentClass);
    }

    private void createMainXml(AndroidProjectFolder projectFile, String mainLayoutName, AssetManager assets) throws IOException {
        if (!mainLayoutName.contains(".")) mainLayoutName += ".xml";
        File layoutMain = new File(projectFile.getDirLayout(), mainLayoutName);
        layoutMain.createNewFile();
        InputStream layoutTemplate = assets.open("templates/src/main/activity_main.xml");
        String contentLayout = FileManager.streamToString(layoutTemplate).toString();
        FileManager.saveFile(layoutMain, contentLayout);
    }

    /**
     * check input data
     *
     * @return true if all is ok
     */
    private boolean isOk() {
        //check app name
        if (editAppName.getText().toString().isEmpty()) {
            editAppName.setError(getString(R.string.enter_name));
            return false;
        }
        String packageName = editPackage.getText().toString();
        if (packageName.isEmpty()) {
            editPackage.setError(getString(R.string.enter_package));
            return false;
        }
        if (!packageName.contains(".")) {
            editPackage.setError("Invalid package name: The package name must be least one '.' separator");
            return false;
        }
        if (!Patterns.PACKAGE_NAME.matcher(packageName).find()) {
            editPackage.setError("Invalid package name");
            return false;
        }

        //check activity name
        String activityName = this.mActivityName.getText().toString();
        if (activityName.isEmpty()) {
            this.mActivityName.setError(getString(R.string.enter_name));
            return false;
        }
        if (!Patterns.RE_IDENTIFIER.matcher(activityName).find()) {
            this.mActivityName.setText("Invalid name");
            return false;
        }

        //check layout name
        if (layoutName.getText().toString().isEmpty()) {
            layoutName.setError(getString(R.string.enter_name));
            return false;
        }
        if (!Patterns.RE_IDENTIFIER.matcher(layoutName.getText().toString()).find()) {
            layoutName.setText("Invalid name");
            return false;
        }

        return true;
    }

}
<file_sep>/app/src/main/java/com/duy/ide/activities/InstallActivity.java
package com.duy.ide.activities;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.duy.ide.R;
import com.duy.ide.file.FileManager;
import com.duy.ide.setting.AppSetting;
import com.jecelyin.android.file_explorer.FileExplorerActivity;

import org.apache.commons.io.IOUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static android.os.Environment.DIRECTORY_DOWNLOADS;

/**
 * Created by Duy on 16-Jul-17.
 */

public class InstallActivity extends AbstractAppCompatActivity implements View.OnClickListener {
    public static final String SYSTEM_VERSION = "System v3.0";
    private static final int REQUEST_CODE_SELECT_FILE = 1101;
    private AppSetting mPreferences;
    private ProgressBar mProgressBar;
    private TextView mInfo;
    private Button mInstallButton;
    //    private TextView mTxtVersion;
    private ProgressDialog progressDialog;
    private boolean mIsInstalling = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_install);
        setupToolbar();
        setTitle(R.string.install);

        mPreferences = new AppSetting(this);
        mProgressBar = findViewById(R.id.progress_bar);
        mInfo = findViewById(R.id.txt_info);
        mInstallButton = findViewById(R.id.btn_install);

//        mTxtVersion = (TextView) findViewById(R.id.txt_version);
//        String version = getString(R.string.system_version) + mPreferences.getSystemVersion();
//        mTxtVersion.setText(version);
        findViewById(R.id.btn_install).setOnClickListener(this);
        findViewById(R.id.btn_select_file).setOnClickListener(this);
        findViewById(R.id.down_load_from_github).setOnClickListener(this);

        extractFileFromAsset();
    }


    @Override
    public void onClick(View v) {
       /* if (v.getId() == R.id.btn_install) {
            if (isConnected()) {
                downloadFile();
            } else {
                showDialogNotConnect();
            }
        } else if (v.getId() == R.id.btn_select_file) {
            selectFile();
        } else if (v.getId() == R.id.down_load_from_github) {
            downloadFromGit();
        }*/
    }

    private void extractFileFromAsset() {
        new CopyFromAssetTask(this).execute();
    }

    private void downloadFromGit() {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.classes_link)));
        try {
            startActivity(intent);
        } catch (Exception e) {
            Toast.makeText(this, "No browser installed", Toast.LENGTH_SHORT).show();
        }
    }

    private void selectFile() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        FileExplorerActivity.startPickFileActivity(this,
                Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS).getPath(), REQUEST_CODE_SELECT_FILE, null);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case REQUEST_CODE_SELECT_FILE:
                if (resultCode == RESULT_OK) {
                    File file = new File(FileExplorerActivity.getFile(data));
                    new InstallTask(this).execute(file);
                }
                break;
        }
    }

    private void showDialogError(Exception e) {
        if (isFinishing()) return;
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.error);
        builder.setMessage(e == null ? " " : e.getMessage());
        builder.create().show();
    }

    private boolean isConnected() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo() != null;
    }

    private void showDialogNotConnect() {

    }

    private void showDialogSuccess() {
        if (isFinishing()) {
            return;
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(R.string.success);
        builder.setPositiveButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                setResult(RESULT_OK);
                finish();
            }
        });
        builder.create().show();
    }

    private void showDialogFailed(Exception error) {
        if (isFinishing()) {
            return;
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.error);
        builder.setMessage(error.getMessage());
        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.create().show();
    }

    @Override
    public void onBackPressed() {
        if (!mIsInstalling) {
            super.onBackPressed();
        }
    }

    private void showDialogDownload() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Download system");
    }

    private void installFailed() {
        Toast.makeText(this, "Install failed", Toast.LENGTH_SHORT).show();
    }

    private class InstallTask extends AsyncTask<File, String, Boolean> {
        private Exception error = null;
        private Context context;

        public InstallTask(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mInfo.setText(R.string.start_install_system);
            mProgressBar.setIndeterminate(true);
            mInstallButton.setEnabled(false);
            mIsInstalling = true;
        }

        @Override
        protected Boolean doInBackground(File... params) {
            try {
                File zip = params[0];
                File outputDir = FileManager.getSdkDir(context);
                unzipArchive(zip, outputDir);
                zip.delete();

                if (!(FileManager.isSdkInstalled(context))) {
                    throw new RuntimeException("Install failed, Not a classes.zip file");
                }
            } catch (Exception e) {
                publishProgress("Error when install system");

                e.printStackTrace();
                error = e;
                return false;
            }


            publishProgress("System install complete!");
            return true;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            mInfo.setText(values[0]);
        }

        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
            if (result) {
                showDialogSuccess();
            } else {
                showDialogFailed(error);
            }
            mInstallButton.setEnabled(true);
            mProgressBar.setIndeterminate(false);
            mIsInstalling = false;
        }

        public void unzipArchive(File archive, File outputDir) {
            try {
                ZipFile zipfile = new ZipFile(archive);
                for (Enumeration e = zipfile.entries(); e.hasMoreElements(); ) {
                    ZipEntry entry = (ZipEntry) e.nextElement();
                    unzipEntry(zipfile, entry, outputDir);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void unzipEntry(ZipFile zipfile, ZipEntry entry, File outputDir) throws IOException {

            if (entry.isDirectory()) {
                createDir(new File(outputDir, entry.getName()));
                return;
            }

            File outputFile = new File(outputDir, entry.getName());
            if (!outputFile.getParentFile().exists()) {
                createDir(outputFile.getParentFile());
            }

            BufferedInputStream inputStream = new BufferedInputStream(zipfile.getInputStream(entry));
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));

            try {
                IOUtils.copy(inputStream, outputStream);
            } finally {
                outputStream.close();
                inputStream.close();
            }
        }

        private void createDir(File dir) {
            if (!dir.mkdirs()) throw new RuntimeException("Can not create dir " + dir);
        }

    }

    private class CopyFromAssetTask extends AsyncTask<File, String, File> {
        private Context context;

        public CopyFromAssetTask(Context context) {
            this.context = context;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mInfo.setText(R.string.copy_from_asset);
            mProgressBar.setIndeterminate(true);
            mInstallButton.setEnabled(false);
            mIsInstalling = true;
        }

        @Override
        protected File doInBackground(File... params) {
            try {
                AssetManager assets = context.getAssets();
                InputStream open = assets.open("android-21/android-21.zip");
                File outFile = new File(getFilesDir(), "classes.zip");
                FileOutputStream fileOutputStream = new FileOutputStream(outFile);
                FileManager.copyStream(open, fileOutputStream);
                fileOutputStream.close();
                return outFile;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(File file) {
            super.onPostExecute(file);
            if (file != null) {
                new InstallTask(context).execute(file);
            } else {
                installFailed();
            }
        }
    }


}
<file_sep>/app/src/main/java/com/duy/ide/editor/code/highlight/HighlightImpl.java
package com.duy.ide.editor.code.highlight;

import com.duy.ide.themefont.themes.database.CodeTheme;

/**
 * Created by Duy on 06-Aug-17.
 */

public abstract class HighlightImpl implements Highlighter {
    protected CodeTheme codeTheme;

    public CodeTheme getCodeTheme() {
        return codeTheme;
    }

    @Override
    public void setCodeTheme(CodeTheme codeTheme) {
        this.codeTheme = codeTheme;
    }

    @Override
    public void setErrorRange(long startPosition, long endPosition) {

    }
}
<file_sep>/settings.gradle
include ':app', ':splitview', ':colorpicker', ':pager', ':treeview', ':dx', ':manifest_merger',
        ':javacodeformatter', ':javacompiler', ':gitforandroid', ':androidlogcat',
        ':fileexplorer', ':common', ':styles'
<file_sep>/app/src/test/java/com/duy/builder/AaptTest.java
package com.duy.builder;

import junit.framework.TestCase;

/**
 * Created by Duy on 22-Aug-17.
 */

public class AaptTest extends TestCase {

    public void test() {

    }

}
<file_sep>/fileexplorer/src/main/java/com/jecelyin/android/file_explorer/util/RootUtils.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.android.file_explorer.util;

import android.text.TextUtils;

import com.jecelyin.common.utils.L;
import com.stericson.RootShell.RootShell;
import com.stericson.RootShell.execution.Command;
import com.stericson.RootShell.execution.Shell;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @author <NAME> <<EMAIL>>
 */
public class RootUtils {
    private static final String TAG = "RootUtils";

    public static void commandWait(Shell shell, Command cmd) throws Exception {
        while (!cmd.isFinished()) {

            synchronized (cmd) {
                try {
                    if (!cmd.isFinished()) {
                        cmd.wait(2000);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if (!cmd.isExecuting() && !cmd.isFinished()) {
                if (!shell.isExecuting && !shell.isReading) {
                    Exception e = new Exception();
                    e.setStackTrace(Thread.currentThread().getStackTrace());
                    e.printStackTrace();
                    L.d(TAG, "Waiting for a command to be executed in a shell that is not executing and not reading! \n\n Command: " + cmd.getCommand(), e);
                } else if (shell.isExecuting && !shell.isReading) {
                    Exception e = new Exception();
                    e.setStackTrace(Thread.currentThread().getStackTrace());
                    e.printStackTrace();
                    L.d(TAG, "Waiting for a command to be executed in a shell that is executing but not reading! \n\n Command: " + cmd.getCommand(), e);
                } else {
                    Exception e = new Exception();
                    e.setStackTrace(Thread.currentThread().getStackTrace());
                    e.printStackTrace();
                    L.d(TAG, "Waiting for a command to be executed in a shell that is not reading! \n\n Command: " + cmd.getCommand(), e);
                }
            }

        }
    }

    public static String getRealPath(String file) {
        List<String> paths = new ArrayList<>();
        File parent = new File(file);

        do {
            paths.add(parent.getName());
        } while ((parent = parent.getParentFile()) != null);

        List<FileInfo> infos;
        FileInfo fi;
        String path;
        StringBuilder sb = new StringBuilder();
        for (int i = paths.size() - 1; i >= 0; i--) {
            path = paths.get(i);

            if ("/".equals(path)) {
                continue;
            }
            sb.append("/").append(path);
            infos = listFileInfo(sb.toString());
            if (infos.isEmpty())
                break;
            fi = infos.get(0);
            if (fi.isSymlink) {
                sb.setLength(0);
                sb.append(fi.linkedPath);
            }
        }

        return sb.toString();
    }

    public static List<FileInfo> listFileInfo(String path) {
        final List<String> result = new ArrayList<>();
        final List<FileInfo> files = new ArrayList<>();

        Command command = new Command(0, false, "ls -la \"" + path + "\"") {
            @Override
            public void commandOutput(int id, String line) {
                RootShell.log(line);
                result.add(line);

                super.commandOutput(id, line);
            }
        };

        try {
            //Try without root...
            Shell shell = RootShell.getShell(true);
            shell.add(command);
            RootUtils.commandWait(shell, command);

        } catch (Exception e) {
            L.e(e);
            return files;
        }

        for (String line : result) {
            line = line.trim();
            // lstat '//persist' failed: Permission denied
            if (line.startsWith("lstat \'" + path) && line.contains("\' failed: Permission denied")) {
                line = line.replace("lstat \'" + path, "");
                line = line.replace("\' failed: Permission denied", "");
                if (line.startsWith("/")) {
                    line = line.substring(1);
                }
                FileInfo failedToRead = new FileInfo(false, line);
                files.add(failedToRead);
                continue;
            }
            // /data/data/com.android.shell/files/bugreports: No such file or directory
            if (line.startsWith("/") && line.contains(": No such file")) {
                continue;
            }
            try {
                files.add(lsParser(path, line));
            } catch (Exception e) {
                L.e("parse line error: " + line, e);
            }
        }

        result.clear();
        return files;
    }

    private static FileInfo lsParser(String path, String line) {
        final String[] split = line.split(" ");
        int index = 0;

        FileInfo file = new FileInfo(false, "");

        String date = "";
        String time = "";
        //drwxrwx--x 3 root sdcard_rw 4096 2016-12-17 15:02 obb
        for (String token : split) {
            if (token.trim().isEmpty())
                continue;
            switch (index) {
                case 0: {
                    file.permissions = token;
                    break;
                }
                case 1: {
                    if (TextUtils.isDigitsOnly(token))
                        continue;
                    file.owner = token;
                    break;
                }
                case 2: {
                    file.group = token;
                    break;
                }
                case 3: {
                    if (token.contains("-")) {
                        // No length, this is the date
                        file.size = -1;
                        date = token;
                    } else if (token.contains(",")) {
                        //In /dev, ls lists the major and minor device numbers
                        file.size = -2;
                    } else {
                        // Length, this is a file
                        try {
                            file.size = Long.parseLong(token);
                        } catch (Exception e) {
                            throw new NumberFormatException(e.getMessage() + " Line: " + line);
                        }
                    }
                    break;
                }
                case 4: {
                    if (file.size == -1) {
                        // This is the time
                        time = token;
                    } else {
                        // This is the date
                        date = token;
                    }
                    break;
                }
                case 5: {
                    if (file.size == -2) {
                        date = token;
                    } else if (file.size > -1) {
                        time = token;
                    }
                    break;
                }
                case 6:
                    if (file.size == -2) {
                        time = token;
                    }
                    break;
            }
            index++;
        }

        if (line.length() > 0) {
            final String nameAndLink = line.substring(line.indexOf(time) + time.length() + 1);
            if (nameAndLink.contains(" -> ")) {
                final String[] splitSl = nameAndLink.split(" -> ");
                file.name = splitSl[0].trim();
                String realPath = splitSl[1].trim();
                if (realPath.charAt(0) != '/') {
                    file.linkedPath = new File(path).getParent() + "/" + realPath;
                } else {
                    file.linkedPath = realPath;
                }
            } else {
                file.name = nameAndLink;
            }
        }

        try {
            file.lastModified = new SimpleDateFormat("yyyy-MM-ddHH:mm", Locale.getDefault())
                    .parse(date + time).getTime();
        } catch (Exception e) {
//            L.e(e); //ignore: java.text.ParseException: Unparseable date: ""
            file.lastModified = 0;
        }

        file.readAvailable = true;
        file.directoryFileCount = "";

        char type = file.permissions.charAt(0);

        if (type == 'd') {
            file.isDirectory = true;
        } else if (type == 'l') {
            file.isSymlink = true;
            String linkPath = file.linkedPath;
            for (; ; ) {
                List<FileInfo> fileInfos = listFileInfo(linkPath);
                if (fileInfos.isEmpty())
                    break;
                FileInfo fi = fileInfos.get(0);
                if (!fi.isSymlink) {
                    file.isDirectory = fi.isDirectory;
                    break;
                }
                linkPath = fi.linkedPath;
            }
        }

        return file;
    }

    public static class RootCommand extends Command {
        private StringBuilder stringBuilder;

        public RootCommand(String commandFormat, Object... args) {
            super(0, true, String.format(commandFormat, args));
            stringBuilder = new StringBuilder();
        }

        @Override
        public void commandOutput(int id, String line) {
            stringBuilder.append(line).append("\n");
            super.commandOutput(id, line);
        }

        @Override
        public void commandCompleted(int id, int exitcode) {
            super.commandCompleted(id, exitcode);

            onFinish(exitcode == 0, stringBuilder.toString());
        }

        public void onFinish(boolean success, String output) {
        }
    }
}
<file_sep>/app/src/main/java/com/duy/project/activities/FileManagerActivity.java
package com.duy.project.activities;

import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;

import com.duy.ide.R;
import com.duy.ide.activities.AbstractAppCompatActivity;


public class FileManagerActivity extends AbstractAppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_file);
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();


    }


}<file_sep>/system/classes/Readme.MD
1. android.jar - Android 19 platform
2. android-support-v4.aar - support-v4-26.0.0-alpha1.aar
3. android-support-v4.jar - support-v4-26.0.0-alpha1-sources.jar
4. android-support-v7.aar - appcompat-v7-26.0.0-alpha1.aar
5. android-support-v7.jar - appcompat-v7-26.0.0-alpha1-sources.jar<file_sep>/app/src/main/java/com/duy/run/activities/ExecuteActivity.java
package com.duy.run.activities;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import android.support.v7.app.AlertDialog;
import android.util.Log;

import com.duy.JavaApplication;
import com.duy.compile.CompileManager;
import com.duy.compile.external.CompileHelper;
import com.duy.ide.R;
import com.duy.ide.activities.AbstractAppCompatActivity;
import com.duy.project.file.java.JavaProjectFolder;
import com.duy.run.view.ConsoleEditText;

import java.io.File;
import java.io.InputStream;

/**
 * Created by Duy on 30-Jul-17.
 */

public class ExecuteActivity extends AbstractAppCompatActivity {
    private static final int RUN_TIME_ERR = 1;
    private static final String TAG = "ExecuteActivity";
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case RUN_TIME_ERR:
                    Exception exception = (Exception) msg.obj;
                    showDialogError(exception.getMessage());
                    break;
            }
        }
    };
    private ConsoleEditText mConsoleEditText;

    private void showDialogError(String message) {
        if (isFinishing()) return;
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(message).setPositiveButton("Close", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        builder.create().show();
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_exec);
        setupToolbar();
        bindView();
        initInOut();
        final Intent intent = getIntent();
        if (intent != null) {
            final JavaProjectFolder projectFile = (JavaProjectFolder) intent.getSerializableExtra(CompileManager.PROJECT_FILE);
            if (projectFile == null) {
                finish();
                return;
            }
            final int action = intent.getIntExtra(CompileManager.ACTION, -1);
            setTitle(projectFile.getMainClass().getSimpleName());

            Thread runThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        runProgram(ExecuteActivity.this, projectFile, action, intent);
                    } catch (Error error) {
                        error.printStackTrace(mConsoleEditText.getErrorStream());
                    } catch (Exception e) {
                        e.printStackTrace(mConsoleEditText.getErrorStream());
                    } catch (Throwable e) {
                        e.printStackTrace(mConsoleEditText.getErrorStream());
                    }
                }
            });
            runThread.start();
        } else {
            finish();
        }
    }

    private void initInOut() {
        JavaApplication application = (JavaApplication) getApplication();
        application.addStdErr(mConsoleEditText.getErrorStream());
        application.addStdOut(mConsoleEditText.getOutputStream());
    }

    @WorkerThread
    private void runProgram(Context context, JavaProjectFolder projectFile, int action, Intent intent) throws Exception {
        InputStream in = mConsoleEditText.getInputStream();

        File tempDir = getDir("dex", MODE_PRIVATE);
        switch (action) {
            case CompileHelper.Action.RUN: {
                CompileHelper.compileAndRun(context, in, tempDir, projectFile);
                break;
            }
            case CompileHelper.Action.RUN_DEX: {
                File dex = (File) intent.getSerializableExtra(CompileManager.DEX_FILE);
                if (dex != null) {
                    String mainClass = projectFile.getMainClass().getName();
                    CompileHelper.executeDex(context, in, dex, tempDir, mainClass);
                }
                break;
            }
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop() called");

        mConsoleEditText.stop();
        JavaApplication application = (JavaApplication) getApplication();
        application.removeErrStream(mConsoleEditText.getErrorStream());
        application.removeOutStream(mConsoleEditText.getOutputStream());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    private void bindView() {
        mConsoleEditText = (ConsoleEditText) findViewById(R.id.console_view);
    }
}
<file_sep>/app/src/test/java/com/duy/ide/autocomplete/autocomplete/TestJavacParser.java
package com.duy.ide.autocomplete.autocomplete;

import com.google.common.collect.ImmutableList;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.parser.Parser;
import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options;

import junit.framework.TestCase;

import java.io.File;
import java.io.IOError;
import java.io.IOException;
import java.net.URI;

import javax.tools.DiagnosticCollector;
import javax.tools.DiagnosticListener;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;

import static com.google.common.base.Charsets.UTF_8;

/**
 * Created by Duy on 15-Aug-17.
 */
public class TestJavacParser extends TestCase {
    private String src = "package com.duy.project.view.dialog;\n" +
            "\n" +
            "import android.app.Activity;\n" +
            "import android.app.Dialog;\n" +
            "import android.content.Context;\n" +
            "import android.os.AsyncTask;\n" +
            "import android.os.Bundle;\n" +
            "import android.support.annotation.NonNull;\n" +
            "import android.support.annotation.Nullable;\n" +
            "import android.support.design.widget.TextInputLayout;\n" +
            "import android.support.v4.widget.SwipeRefreshLayout;\n" +
            "import android.support.v7.app.AlertDialog;\n" +
            "import android.support.v7.app.AppCompatDialogFragment;\n" +
            "import android.support.v7.widget.DividerItemDecoration;\n" +
            "import android.support.v7.widget.LinearLayoutManager;\n" +
            "import android.support.v7.widget.RecyclerView;\n" +
            "import android.text.TextUtils;\n" +
            "import android.view.LayoutInflater;\n" +
            "import android.view.View;\n" +
            "import android.view.ViewGroup;\n" +
            "import android.view.Window;\n" +
            "import android.view.WindowManager;\n" +
            "import android.widget.Button;\n" +
            "import android.widget.EditText;\n" +
            "import android.widget.TextView;\n" +
            "import android.widget.Toast;\n" +
            "\n" +
            "import com.duy.ide.R;\n" +
            "import com.duy.ide.file.FileManager;\n" +
            "import com.duy.ide.file.FileSelectListener;\n" +
            "import com.duy.ide.file.PreferenceHelper;\n" +
            "import com.duy.ide.file.adapter.FileAdapterListener;\n" +
            "import com.duy.ide.file.adapter.FileDetail;\n" +
            "import com.duy.ide.file.adapter.FileListAdapter;\n" +
            "import com.duy.ide.utils.Build;\n" +
            "\n" +
            "import org.apache.commons.io.FileUtils;\n" +
            "import org.apache.commons.io.FilenameUtils;\n" +
            "\n" +
            "import java.io.File;\n" +
            "import java.text.SimpleDateFormat;\n" +
            "import java.util.LinkedList;\n" +
            "import java.util.Locale;\n" +
            "\n" +
            "/**\n" +
            " * Created by Duy on 17-Jul-17.\n" +
            " */\n" +
            "\n" +
            "public class DialogSelectDirectory extends AppCompatDialogFragment implements View.OnClickListener, View.OnLongClickListener,\n" +
            "        SwipeRefreshLayout.OnRefreshListener, FileAdapterListener {\n" +
            "    public static final String TAG = \"DialogSelectDirectory\";\n" +
            "\n" +
            "    private FileSelectListener listener;\n" +
            "    private RecyclerView listFiles;\n" +
            "    private Activity activity;\n" +
            "    private String currentFolder;\n" +
            "    private boolean wantAFile = false;\n" +
            "    private SwipeRefreshLayout swipeRefreshLayout;\n" +
            "    @Nullable\n" +
            "    private FileListAdapter mAdapter;\n" +
            "    private TextView txtPath;\n" +
            "    private int request;\n" +
            "\n" +
            "    public static DialogSelectDirectory newInstance(String path, int request) {\n" +
            "\n" +
            "        Bundle args = new Bundle();\n" +
            "        args.putString(\"path\", path);\n" +
            "        args.putInt(\"request\", request);\n" +
            "        DialogSelectDirectory fragment = new DialogSelectDirectory();\n" +
            "        fragment.setArguments(args);\n" +
            "        return fragment;\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onResume() {\n" +
            "        super.onResume();\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onAttach(Context context) {\n" +
            "        super.onAttach(context);\n" +
            "        try {\n" +
            "            listener = (FileSelectListener) getActivity();\n" +
            "        } catch (Exception ignored) {\n" +
            "\n" +
            "        }\n" +
            "        activity = getActivity();\n" +
            "        request = getArguments().getInt(\"request\");\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onStart() {\n" +
            "        super.onStart();\n" +
            "        Dialog dialog = getDialog();\n" +
            "        if (dialog != null) {\n" +
            "            Window window = dialog.getWindow();\n" +
            "            if (window != null) window.setLayout(WindowManager.LayoutParams.MATCH_PARENT,\n" +
            "                    WindowManager.LayoutParams.MATCH_PARENT);\n" +
            "        }\n" +
            "    }\n" +
            "\n" +
            "    @Nullable\n" +
            "    @Override\n" +
            "    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n" +
            "        return inflater.inflate(R.layout.dialog_choose_file, container, false);\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n" +
            "        super.onViewCreated(view, savedInstanceState);\n" +
            "\n" +
            "        currentFolder = getArguments().getString(\"path\", FileManager.EXTERNAL_DIR);\n" +
            "        wantAFile = false;\n" +
            "\n" +
            "        bindView(view);\n" +
            "\n" +
            "        //load file\n" +
            "        new UpdateList(currentFolder).execute();\n" +
            "    }\n" +
            "\n" +
            "    private void bindView(View view) {\n" +
            "        listFiles = view.findViewById(R.id.list_file);\n" +
            "        listFiles.setHasFixedSize(true);\n" +
            "        listFiles.setLayoutManager(new LinearLayoutManager(activity));\n" +
            "        listFiles.addItemDecoration(new DividerItemDecoration(getContext(),\n" +
            "                DividerItemDecoration.VERTICAL));\n" +
            "\n" +
            "        swipeRefreshLayout = view.findViewById(R.id.refresh_view);\n" +
            "        swipeRefreshLayout.setOnRefreshListener(this);\n" +
            "\n" +
            "        view.findViewById(R.id.action_new_folder).setOnClickListener(this);\n" +
            "        txtPath = view.findViewById(R.id.txt_path);\n" +
            "        view.findViewById(R.id.btn_select).setOnClickListener(new View.OnClickListener() {\n" +
            "            @Override\n" +
            "            public void onClick(View view) {\n" +
            "                if (listener != null) {\n" +
            "                    listener.onFileSelected(new File(currentFolder), request);\n" +
            "                    dismiss();\n" +
            "                }\n" +
            "            }\n" +
            "        });\n" +
            "    }\n" +
            "\n" +
            "    private void createNewFolder() {\n" +
            "        AlertDialog.Builder builder = new AlertDialog.Builder(activity);\n" +
            "        builder.setTitle(R.string.new_folder);\n" +
            "        builder.setView(R.layout.dialog_new_file);\n" +
            "        final AlertDialog alertDialog = builder.create();\n" +
            "        alertDialog.show();\n" +
            "        final EditText editText = (EditText) alertDialog.findViewById(R.id.edit_file_name);\n" +
            "        final TextInputLayout textInputLayout = (TextInputLayout) alertDialog.findViewById(R.id.hint);\n" +
            "        assert textInputLayout != null;\n" +
            "        textInputLayout.setHint(getString(R.string.enter_new_folder_name));\n" +
            "        Button btnOK = null;\n" +
            "//        btnOK = (Button) alertDialog.findViewById(R.id.btn_ok);\n" +
            "        Button btnCancel = (Button) alertDialog.findViewById(R.id.btn_cancel);\n" +
            "        btnCancel.setOnClickListener(new View.OnClickListener() {\n" +
            "            @Override\n" +
            "            public void onClick(View v) {\n" +
            "                alertDialog.cancel();\n" +
            "            }\n" +
            "        });\n" +
            "        btnOK.setOnClickListener(new View.OnClickListener() {\n" +
            "            @Override\n" +
            "            public void onClick(View v) {\n" +
            "                //get string path of in edit text\n" +
            "                String fileName = editText != null ? editText.getText().toString() : null;\n" +
            "                if (fileName.isEmpty()) {\n" +
            "                    editText.setError(getString(R.string.enter_new_file_name));\n" +
            "                    return;\n" +
            "                }\n" +
            "                //create new file\n" +
            "                File file = new File(currentFolder, fileName);\n" +
            "                file.mkdirs();\n" +
            "                new UpdateList(currentFolder).execute();\n" +
            "                alertDialog.cancel();\n" +
            "            }\n" +
            "        });\n" +
            "\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onClick(View v) {\n" +
            "        int i = v.getId();\n" +
            "        if (i == R.id.action_new_folder) {\n" +
            "            createNewFolder();\n" +
            "        }\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public boolean onLongClick(View v) {\n" +
            "        return false;\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onRefresh() {\n" +
            "        new UpdateList(currentFolder).execute();\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onItemClick(View v, String name, int action) {\n" +
            "        if (action == ACTION_LONG_CLICK) {\n" +
            "            if (name.equals(\"..\")) {\n" +
            "                if (currentFolder.equals(\"/\")) {\n" +
            "                    new UpdateList(PreferenceHelper.getWorkingFolder(activity)).execute();\n" +
            "                } else {\n" +
            "                    File tempFile = new File(currentFolder);\n" +
            "                    if (tempFile.isFile()) {\n" +
            "                        tempFile = tempFile.getParentFile()\n" +
            "                                .getParentFile();\n" +
            "                    } else {\n" +
            "                        tempFile = tempFile.getParentFile();\n" +
            "                    }\n" +
            "                    new UpdateList(tempFile.getAbsolutePath()).execute();\n" +
            "                }\n" +
            "            } else if (name.equals(getString(R.string.home))) {\n" +
            "                // TODO: 14-Mar-17\n" +
            "                new UpdateList(PreferenceHelper.getWorkingFolder(activity)).execute();\n" +
            "            }\n" +
            "\n" +
            "//            final File selectedFile = new File(currentFolder, name);\n" +
            "\n" +
            "//            if (selectedFile.isFile() && wantAFile) {\n" +
            "//                // TODO: 15-Mar-17\n" +
            "//                if (listener != null) listener.onFileLongClick(selectedFile);\n" +
            "//            } else if (selectedFile.isDirectory()) {\n" +
            "//            }\n" +
            "        } else if (action == ACTION_CLICK) {\n" +
            "            if (name.equals(\"..\")) {\n" +
            "                if (currentFolder.equals(\"/\")) {\n" +
            "                    new UpdateList(PreferenceHelper.getWorkingFolder(activity)).execute();\n" +
            "                } else {\n" +
            "                    File tempFile = new File(currentFolder);\n" +
            "                    if (tempFile.isFile()) {\n" +
            "                        tempFile = tempFile.getParentFile()\n" +
            "                                .getParentFile();\n" +
            "                    } else {\n" +
            "                        tempFile = tempFile.getParentFile();\n" +
            "                    }\n" +
            "                    new UpdateList(tempFile.getAbsolutePath()).execute();\n" +
            "                }\n" +
            "                return;\n" +
            "            } else if (name.equals(getString(R.string.home))) {\n" +
            "                // TODO: 14-Mar-17\n" +
            "                new UpdateList(PreferenceHelper.getWorkingFolder(activity)).execute();\n" +
            "                return;\n" +
            "            }\n" +
            "\n" +
            "            final File selectedFile = new File(currentFolder, name);\n" +
            "//\n" +
            "//            if (selectedFile.isFile() && wantAFile) {\n" +
            "//                // TODO: 15-Mar-17\n" +
            "//                if (listener != null) listener.onFileSelected(selectedFile, request);\n" +
            "//            } else if (selectedFile.isDirectory()) {\n" +
            "            new UpdateList(selectedFile.getAbsolutePath()).execute();\n" +
            "//            }\n" +
            "        }\n" +
            "    }\n" +
            "\n" +
            "\n" +
            "    public void refresh() {\n" +
            "        new UpdateList(currentFolder).execute();\n" +
            "    }\n" +
            "\n" +
            "    @Override\n" +
            "    public void onRemoveClick(View view, String name, int action) {\n" +
            "        Toast.makeText(activity, \"Don't support this action\", Toast.LENGTH_SHORT).show();\n" +
            "    }\n" +
            "\n" +
            "\n" +
            "    private class UpdateList extends AsyncTask<Void, Void, LinkedList<FileDetail>> {\n" +
            "        private String path;\n" +
            "        private String exceptionMessage;\n" +
            "\n" +
            "        public UpdateList(@NonNull String path) {\n" +
            "            this.path = path;\n" +
            "        }\n" +
            "\n" +
            "        @Override\n" +
            "        protected void onPreExecute() {\n" +
            "            super.onPreExecute();\n" +
            "            txtPath.setText(path);\n" +
            "        }\n" +
            "\n" +
            "        @Override\n" +
            "        protected LinkedList<FileDetail> doInBackground(final Void... params) {\n" +
            "            try {\n" +
            "                if (TextUtils.isEmpty(path)) {\n" +
            "                    return null;\n" +
            "                }\n" +
            "\n" +
            "                File tempFolder = new File(path);\n" +
            "                if (tempFolder.isFile()) {\n" +
            "                    tempFolder = tempFolder.getParentFile();\n" +
            "                }\n" +
            "\n" +
            "                String[] canOpen = {\"java\", \"class\", \"jar\"};\n" +
            "\n" +
            "                final LinkedList<FileDetail> fileDetails = new LinkedList<>();\n" +
            "                final LinkedList<FileDetail> folderDetails = new LinkedList<>();\n" +
            "                currentFolder = tempFolder.getAbsolutePath();\n" +
            "\n" +
            "                if (!tempFolder.canRead()) {\n" +
            "\n" +
            "                } else {\n" +
            "                    File[] files = tempFolder.listFiles();\n" +
            "                    for (final File f : files) {\n" +
            "                        if (f.isDirectory() && !f.getName().equalsIgnoreCase(\"fonts\")) {\n" +
            "                            folderDetails.add(new FileDetail(f.getName(), getString(R.string.folder), \"\"));\n" +
            "                        } else if (f.isFile()\n" +
            "                                && FilenameUtils.isExtension(f.getName().toLowerCase(), canOpen)\n" +
            "                                && FileUtils.sizeOf(f) <= Build.MAX_FILE_SIZE * FileUtils.ONE_KB) {\n" +
            "                            final long fileSize = f.length();\n" +
            "                            SimpleDateFormat format = new SimpleDateFormat(\"MMM dd, yyyy  hh:mm a\", Locale.getDefault());\n" +
            "                            String date = format.format(f.lastModified());\n" +
            "                            fileDetails.add(new FileDetail(f.getName(),\n" +
            "\n" +
            "\n" +
            "        @Override\n" +
            "        protected void onPostExecute(final LinkedList<FileDetail> names) {\n" +
            "            if (names != null) {\n" +
            "                boolean isRoot = currentFolder.equals(\"/\");\n" +
            "            }\n" +
            "            super.onPostExecute(names);\n" +
            "        }\n" +
            "    }\n" +
            "\n" +
            "\n" +
            "}";

    public void test1() {
        Context context = new Context();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        context.put(DiagnosticListener.class, diagnostics);
        Options.instance(context).put("allowStringFolding", "false");
        JCTree.JCCompilationUnit unit;
        JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
        try {
            fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
        } catch (IOException e) {
            // impossible
            throw new IOError(e);
        }
        SimpleJavaFileObject source =
                new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
                    @Override
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
                        return src;
                    }
                };
        Log.instance(context).useSource(source);
        ParserFactory parserFactory = ParserFactory.instance(context);
        Parser parser =
                parserFactory.newParser(
                        src,
            /*keepDocComments=*/ true,
            /*keepEndPos=*/ true,
            /*keepLineMap=*/ true);
        unit = parser.parseCompilationUnit();
        unit.sourcefile = source;
    }

    public void test4() {
        Context context = new Context();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        context.put(DiagnosticListener.class, diagnostics);
        Options.instance(context).put("allowStringFolding", "false");
        JCTree.JCCompilationUnit unit;
        JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
        try {
            fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
        } catch (IOException e) {
            // impossible
            throw new IOError(e);
        }
        SimpleJavaFileObject source =
                new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
                    @Override
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
                        return src;
                    }
                };
        Log.instance(context).useSource(source);
        ParserFactory parserFactory = ParserFactory.instance(context);
        Parser parser =
                parserFactory.newParser(
                        src,
            /*keepDocComments=*/ true,
            /*keepEndPos=*/ true,
            /*keepLineMap=*/ true);
        unit = parser.parseCompilationUnit();
        unit.sourcefile = source;
    }

    public void test3() {
        Context context = new Context();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        context.put(DiagnosticListener.class, diagnostics);
        Options.instance(context).put("allowStringFolding", "false");
        JCTree.JCCompilationUnit unit;
        JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
        try {
            fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
        } catch (IOException e) {
            // impossible
            throw new IOError(e);
        }
        SimpleJavaFileObject source =
                new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
                    @Override
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
                        return src;
                    }
                };
        Log.instance(context).useSource(source);
        ParserFactory parserFactory = ParserFactory.instance(context);
        Parser parser =
                parserFactory.newParser(
                        src,
            /*keepDocComments=*/ true,
            /*keepEndPos=*/ true,
            /*keepLineMap=*/ true);
        unit = parser.parseCompilationUnit();
        unit.sourcefile = source;
    }

    public void test2() {
        Context context = new Context();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        context.put(DiagnosticListener.class, diagnostics);
        Options.instance(context).put("allowStringFolding", "false");
        JCTree.JCCompilationUnit unit;
        JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
        try {
            fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.<File>of());
        } catch (IOException e) {
            // impossible
            throw new IOError(e);
        }
        SimpleJavaFileObject source =
                new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
                    @Override
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
                        return src;
                    }
                };
        Log.instance(context).useSource(source);
        ParserFactory parserFactory = ParserFactory.instance(context);
        Parser parser =
                parserFactory.newParser(
                        src,
            /*keepDocComments=*/ true,
            /*keepEndPos=*/ true,
            /*keepLineMap=*/ true);
        unit = parser.parseCompilationUnit();
        unit.sourcefile = source;
    }
}<file_sep>/app/src/main/java/com/duy/ide/activities/DonateActivity.java
package com.duy.ide.activities;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Toast;

import com.duy.ide.BuildConfig;
import com.duy.ide.DLog;
import com.duy.ide.R;
import com.google.firebase.analytics.FirebaseAnalytics;

import aidl.util.IabBroadcastReceiver;
import aidl.util.IabHelper;
import aidl.util.IabResult;
import aidl.util.Inventory;
import aidl.util.Purchase;

import static com.duy.ide.utils.DonateUtils.REQUEST_DONATE;

/**
 * Created by Duy on 11-Aug-17.
 */

public class DonateActivity extends AbstractAppCompatActivity implements IabBroadcastReceiver.IabBroadcastListener, View.OnClickListener {
    private static final String SKU_DONATE_ONE = "java_nide_donate_one_dollar";
    private static final String SKU_DONATE_TWO = "java_nide_donate_two_dollar";
    private static final String SKU_DONATE_THREE = "java_nide_donate_three_dollar";
    private static final String SKU_DONATE_FOUR = "java_nide_donate_four_dollar";
    private static final String SKU_DONATE_FIVE = "java_nide_donate_five_dollar";

    private static final String TAG = "DonateActivity";
    private Handler mHandler = new Handler();
    private IabHelper mIabHelper;
    private IabBroadcastReceiver mBroadcastReceiver;
    private IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        @Override
        public void onQueryInventoryFinished(IabResult result, Inventory inv) {
            if (mIabHelper == null) return;
            if (result.isFailure()) {
                iabError(new RuntimeException("Failed to query inventory: " + result));
            }
            DLog.d(TAG, "Query inventory was successful. ");
            if (inv != null) {
                Purchase premiumPurchase = inv.getPurchase(SKU_DONATE_ONE);
                DLog.d(TAG, "onQueryInventoryFinished: " + premiumPurchase);
                boolean success = premiumPurchase != null && verifyDeveloperPayload(premiumPurchase);
                if (success) {
                    Toast.makeText(DonateActivity.this, "Thank for donate me", Toast.LENGTH_SHORT).show();
                }
            }
        }
    };
    private aidl.util.IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
            = new IabHelper.OnIabPurchaseFinishedListener() {
        @Override
        public void onIabPurchaseFinished(IabResult result, Purchase info) {
            if (mIabHelper == null) return;

            if (result.isFailure()) {
                iabError(new RuntimeException("Error purchasing: " + result));
                return;
            }

            if (!verifyDeveloperPayload(info)) {
                iabError(new RuntimeException("Error purchasing. Authenticity verification failed."));
                return;
            }
            DLog.d(TAG, "Purchase successful.");
            if (info.getSku().equals(SKU_DONATE_ONE)) {
                Toast.makeText(DonateActivity.this, "Thank for donate me", Toast.LENGTH_SHORT).show();
            }
        }
    };

    private boolean verifyDeveloperPayload(Purchase premiumPurchase) {
        String developerPayload = premiumPurchase.getDeveloperPayload();
        return true;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_donate);
        setupToolbar();
        setTitle(R.string.donate_for_project);
        findViewById(R.id.btn_donate_1).setOnClickListener(this);
        findViewById(R.id.btn_donate_2).setOnClickListener(this);
        findViewById(R.id.btn_donate_3).setOnClickListener(this);
        findViewById(R.id.btn_donate_4).setOnClickListener(this);
        findViewById(R.id.btn_donate_5).setOnClickListener(this);

        initIab();
    }

    private void initIab() {
        String base64Key = "MIIBIjANBgkqhkiG<KEY>IIBCgKCAQEAh+uvYlgvcAOOi9fTL8AUN7KDGq" +
                "j3diAZ+5V6d5Ovl7QJNMuDJJpUEsMHzj/fpd/NO7k2gevNLGwX1JKM5BhWLDq/FHB8xTHT1yalZ69fhPPN" +
                "tqCENtdtOTFj4owfzgCSW2TL2gBWzh/JpyBXH9rYyKsgcm5SNg5zA2uCp7Sb5yQDSLFCIiEkrriDPQS0GR" +
                "c<KEY>hcBiTeFAkyJ1pDdweaiNkcQowR04YOQt/NqF4X6peBs+5a4jzZ6UQWvl/YZ+yxz" +
                "8<KEY>";
        mIabHelper = new IabHelper(this, base64Key);
        mIabHelper.enableDebugLogging(BuildConfig.DEBUG);
        mIabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            @Override
            public void onIabSetupFinished(IabResult result) {
                if (!result.isSuccess()) {
                    iabError(null);
                    return;
                }
                if (mIabHelper == null) return;

                mBroadcastReceiver = new IabBroadcastReceiver(DonateActivity.this);
                IntentFilter intentFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
                registerReceiver(mBroadcastReceiver, intentFilter);
                DLog.d(TAG, "onIabSetupFinished: setup success");

                try {
                    mIabHelper.queryInventoryAsync(mGotInventoryListener);
                } catch (Exception e) {
                    iabError(e);
                }
            }
        });

    }

    private void iabError(Exception e) {
        if (e != null) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (!mIabHelper.handleActivityResult(requestCode, resultCode, data)) {

        } else {
            DLog.d(TAG, "onActivityResult handled by IABUtil.");
        }

    }

    private void purchase(String sku) {
        FirebaseAnalytics.getInstance(this).logEvent("purchase" + sku, new Bundle());
        String payload = "";
        try {
            mIabHelper.launchPurchaseFlow(this, sku, REQUEST_DONATE, mPurchaseFinishedListener, payload);
        } catch (Exception e) {
            iabError(e);
        }
    }

    @Override
    public void receivedBroadcast() {
        try {
            mIabHelper.queryInventoryAsync(mGotInventoryListener);
        } catch (IabHelper.IabAsyncInProgressException e) {
            iabError(e);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        DLog.d(TAG, "onDestroy() called");

        if (mBroadcastReceiver != null) {
            unregisterReceiver(mBroadcastReceiver);
        }
        try {
            if (mIabHelper != null) {
                mIabHelper.disposeWhenFinished();
                mIabHelper = null;
            }
        } catch (Exception e) {
            //iab unregister
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_donate_1:
                purchase(SKU_DONATE_ONE);
                break;
            case R.id.btn_donate_2:
                purchase(SKU_DONATE_TWO);
                break;
            case R.id.btn_donate_3:
                purchase(SKU_DONATE_THREE);
                break;
            case R.id.btn_donate_4:
                purchase(SKU_DONATE_FOUR);
                break;
            case R.id.btn_donate_5:
                purchase(SKU_DONATE_FIVE);
                break;
        }
    }
}
<file_sep>/app/src/main/java/com/duy/ide/command/Commander.java
package com.duy.ide.command;

import java.util.ArrayList;

/**
 * Created by Duy on 20-Dec-17.
 */

public class Commander implements ICommand {
    private ArrayList<ICommand> commands = new ArrayList<>();

    public void addCommand(ICommand command) {
        commands.add(command);
    }

    public ArrayList<ICommand> getCommands() {
        return commands;
    }

    @Override
    public boolean execute(Object... params) {
        for (ICommand command : commands) {
            boolean execute = command.execute(params);
            if (!execute) {
                System.out.printf("Task %s executed with failed result%n", command);
                return false;
            }
        }
        System.out.println("Task" + this + " executed successful!");
        return true;
    }

    @Override
    public String toString() {
        return super.toString();
    }
}
<file_sep>/app/src/main/java/com/duy/compile/external/android/AndroidBuilder.java
package com.duy.compile.external.android;

import android.content.Context;
import android.util.Log;

import com.android.annotations.NonNull;
import com.android.sdklib.build.ApkBuilderMain;
import com.duy.compile.external.CompileHelper;
import com.duy.ide.DLog;
import com.duy.ide.file.FileManager;
import com.duy.project.file.android.AndroidProjectFolder;
import com.duy.project.file.android.KeyStore;
import com.sun.tools.javac.main.Main;

import java.io.File;
import java.util.Arrays;

import javax.tools.DiagnosticCollector;

import kellinwood.security.zipsigner.ProgressEvent;
import kellinwood.security.zipsigner.ZipSigner;
import kellinwood.security.zipsigner.optional.CustomKeySigner;

import static com.duy.compile.external.android.util.S.dirLibs;


public class AndroidBuilder {
    private static final String TAG = "BuildTask";

    private static void buildApk(AndroidProjectFolder projectFile) throws Exception {
        String[] args = {
                projectFile.getApkUnsigned().getPath(),
                "-v",
                "-u",
                "-z", projectFile.getResourceFile().getPath(),
                "-f", projectFile.getDexedClassesFile().getPath()
        };
        DLog.d(TAG, "buildApk args = " + Arrays.toString(args));
        ApkBuilderMain.main(args);
    }

    public static void build(Context context, AndroidProjectFolder projectFile,
                             @NonNull DiagnosticCollector diagnosticCollector) throws Exception {
        AndroidBuilder.extractLibrary(projectFile);

        //create R.java
        System.out.println("Run aidl");
        AndroidBuilder.runAidl(projectFile);
        System.out.println("Run aapt");
        AndroidBuilder.runAapt(context, projectFile);

        //compile java
        System.out.println("Compile Java file");
        int status = CompileHelper.compileJava(context, projectFile, diagnosticCollector);
        System.gc();
        if (status != Main.EXIT_OK) {
            System.out.println("Compile error");
            throw new RuntimeException("Compile time error!");
        }

        //classes to dex
        System.out.println("Convert classes to dex");
        CompileHelper.convertToDexFormat(context, projectFile);

        //zip apk
        System.out.println("Build apk");
        AndroidBuilder.buildApk(projectFile);

        System.out.println("Zip sign");
        AndroidBuilder.zipSign(projectFile);

        System.out.println("Zip align");
        AndroidBuilder.zipAlign();

        System.out.println("Publish apk");
        AndroidBuilder.publishApk();
    }

    private static void extractLibrary(AndroidProjectFolder projectFolder) {
        File[] files = dirLibs.listFiles();
        if (files != null) {
            for (File lib : files) {
                if (lib.isFile() && lib.getPath().endsWith(".aar")) {
                }
            }
        }
    }

    private static void runAidl(AndroidProjectFolder projectFile) throws Exception {
        Log.d(TAG, "runAidl() called");

        // TODO make aidl.so
    }

    private static void runAapt(Context context, AndroidProjectFolder projectFile) throws Exception {
        Log.d(TAG, "runAapt() called");

        com.duy.aapt.Aapt aapt = new com.duy.aapt.Aapt();
        StringBuilder command = new StringBuilder("aapt p -f --auto-add-overlay"
                //"-v" + //print info
                + " -M " + projectFile.getXmlManifest().getPath()  //manifest file
                + " -F " + projectFile.getResourceFile().getPath()  //output resources.ap_
                + " -I " + FileManager.getClasspathFile(context).getPath()  //include
                + " -A " + projectFile.getDirAssets().getPath()  //input assets dir
                + " -S " + projectFile.getDirRes().getPath()  //input resource dir
                + " -J " + projectFile.getClassR().getParent());//parent file of R.java file

        //test
//        File appcompatDir = new File(Environment.getExternalStorageDirectory(), ".JavaNIDE/appcompat-v7-21.0.0");
//        File appcompatRes = new File(appcompatDir, "res");
//        File appcompatAsset = new File(appcompatDir, "assets");
//        command.append(" -S ").append(appcompatRes.getPath());
//        command.append(" -A ").append(appcompatAsset.getPath());

        File dirLibs = projectFile.getDirLibs();
        File[] files = dirLibs.listFiles();
        if (files != null) {
            for (File lib : files) {
                if (lib.isFile() && false) {
                    if (lib.getPath().endsWith(".jar")) {
                        command.append(" -I ").append(lib.getPath());
                    } else if (lib.getPath().endsWith(".aar")) {
                        command.append(" -I ").append(lib.getPath()).append(File.separator).append("res");
                    }
                }
            }
        }
        Log.d(TAG, "runAapt command = " + command);
        int exitCode = aapt.fnExecute(command.toString());
        if (exitCode != 0) {
            throw new Exception("AAPT exit(" + exitCode + ")");
        }

    }

    private static void zipSign(AndroidProjectFolder projectFile) throws Exception {
//        if (!appContext.getString(R.string.keystore).contentEquals(projectFile.jksEmbedded.getName())) {
//             TODO use user defined certificate
//        }

        // use embedded private key
        KeyStore keyStore = projectFile.getKeyStore();
        String keystorePath = keyStore.getFile().getPath();
        char[] keystorePw = keyStore.getPassword();
        String certAlias = keyStore.getCertAlias();
        char[] certPw = keyStore.getCertPassword();
        String signatureAlgorithm = "SHA1withRSA";

        ZipSigner zipsigner = new ZipSigner();
        zipsigner.addProgressListener(new SignProgress() {
            @Override
            public void onProgress(ProgressEvent event) {
                super.onProgress(event);
                System.out.println("Sign progress: " + event.getPercentDone());
            }
        });
        CustomKeySigner.signZip(zipsigner, keystorePath, keystorePw, certAlias,
                certPw, signatureAlgorithm,
                projectFile.getApkUnsigned().getPath(),
                projectFile.getApkUnaligned().getPath());
    }


    private static void zipAlign() throws Exception {
//         TODO make zipalign.so
    }

    private static void publishApk() throws Exception {
//        if (projectFile.apkRedistributable.exists()) {
//            projectFile.apkRedistributable.delete();
//        }
//        Util.copy(projectFile.apkUnaligned, new FileOutputStream(projectFile.apkRedistributable));
//
//        projectFile.apkRedistributable.setReadable(true, false);
    }

    public void run() {


    }

    public static class SignProgress implements kellinwood.security.zipsigner.ProgressListener {

        public void onProgress(kellinwood.security.zipsigner.ProgressEvent event) {
        }

    }

}<file_sep>/app/src/main/java/com/duy/ide/localdata/DatabaseHelper.java
package com.duy.ide.localdata;

/**
 * Created by Duy on 12-Aug-17.
 */

public class DatabaseHelper {
}
<file_sep>/app/src/main/java/com/duy/ide/editor/uidesigner/dynamiclayoutinflator/DynamicLayoutInflator.java
package com.duy.ide.editor.uidesigner.dynamiclayoutinflator;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Copyright <NAME> 2015.
 * Source: https://github.com/nickwah/DynamicLayoutInflator
 * <p>
 * Licensed under the MIT License:
 * <p>
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * <p>
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * <p>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
public class DynamicLayoutInflator {
    public static final int NO_LAYOUT_RULE = -999;
    public static final String[] CORNERS = {"TopLeft", "TopRight", "BottomRight", "BottomLeft"};
    private static final String ns = null;
    public static int highestIdNumberUsed = 1234567;
    public static Map<String, ViewParamRunnable> viewRunnables;
    private static ImageLoader imageLoader = null;

    public static void setImageLoader(ImageLoader il) {
        imageLoader = il;
    }

    public static void setDelegate(View root, Object delegate) {
        DynamicLayoutInfo info;
        if (root.getTag() == null || !(root.getTag() instanceof DynamicLayoutInfo)) {
            info = new DynamicLayoutInfo();
            root.setTag(info);
        } else {
            info = (DynamicLayoutInfo) root.getTag();
        }
        info.delegate = delegate;
    }

    public static View inflateName(Context context, String name) {
        return inflateName(context, name, null);
    }

    public static View inflateName(Context context, String name, ViewGroup parent) {
        if (name.startsWith("<")) {
            // Assume it's XML
            return DynamicLayoutInflator.inflate(context, name, parent);
        } else {
            File savedFile = context.getFileStreamPath(name + ".xml");
            try {
                InputStream fileStream = new FileInputStream(savedFile);
                return DynamicLayoutInflator.inflate(context, fileStream, parent);
            } catch (FileNotFoundException e) {
            }
            try {
                InputStream assetStream = context.getAssets().open(name + ".xml");
                return DynamicLayoutInflator.inflate(context, assetStream, parent);
            } catch (IOException e) {
            }
            int id = context.getResources().getIdentifier(name, "layout", context.getPackageName());
            if (id > 0) {
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                return inflater.inflate(id, parent, false);
            }
        }
        return null;
    }

    public static View inflate(Context context, File xmlPath) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(xmlPath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return DynamicLayoutInflator.inflate(context, inputStream);
    }

    public static View inflate(Context context, String xml) {
        InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
        return DynamicLayoutInflator.inflate(context, inputStream);
    }

    public static View inflate(Context context, String xml, ViewGroup parent) {
        InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
        return DynamicLayoutInflator.inflate(context, inputStream, parent);
    }

    public static View inflate(Context context, InputStream inputStream) {
        return inflate(context, inputStream, null);
    }

    public static View inflate(Context context, InputStream inputStream, ViewGroup parent) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.parse(inputStream);
            try {
                return inflate(context, document.getDocumentElement(), parent);
            } finally {
                inputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static View inflate(Context context, Node node) {
        return inflate(context, node, null);
    }

    public static View inflate(Context context, Node node, ViewGroup parent) {
        View mainView = getViewForName(context, node.getNodeName());
        if (parent != null)
            parent.addView(mainView); // have to add to parent to enable certain layout attrs
        applyAttributes(mainView, getAttributesMap(node), parent);
        if (mainView instanceof ViewGroup && node.hasChildNodes()) {
            parseChildren(context, node, (ViewGroup) mainView);
        }
        return mainView;
    }

    private static void parseChildren(Context context, Node node, ViewGroup mainView) {
        NodeList nodeList = node.getChildNodes();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node currentNode = nodeList.item(i);
            if (currentNode.getNodeType() != Node.ELEMENT_NODE) continue;
            inflate(context, currentNode, mainView); // this recursively can call parseChildren
        }
    }

    private static View getViewForName(Context context, String name) {
        try {
            if (!name.contains(".")) {
                name = "android.widget." + name;
            }
            Class<?> clazz = Class.forName(name);
            Constructor<?> constructor = clazz.getConstructor(Context.class);
            return (View) constructor.newInstance(context);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static HashMap<String, String> getAttributesMap(Node currentNode) {
        NamedNodeMap attributeMap = currentNode.getAttributes();
        int attributeCount = attributeMap.getLength();
        HashMap<String, String> attributes = new HashMap<>(attributeCount);
        for (int j = 0; j < attributeCount; j++) {
            Node attr = attributeMap.item(j);
            String nodeName = attr.getNodeName();
            if (nodeName.startsWith("android:")) nodeName = nodeName.substring(8);
            attributes.put(nodeName, attr.getNodeValue());
        }
        return attributes;
    }

    @SuppressLint("NewApi")
    private static void applyAttributes(View view, Map<String, String> attrs, ViewGroup parent) {
        if (viewRunnables == null) createViewRunnables();
        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
        int layoutRule;
        int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0,
                paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
        boolean hasCornerRadius = false, hasCornerRadii = false;
        for (Map.Entry<String, String> entry : attrs.entrySet()) {
            String attr = entry.getKey();
            if (viewRunnables.containsKey(attr)) {
                viewRunnables.get(attr).apply(view, entry.getValue(), parent, attrs);
                continue;
            }
            if (attr.startsWith("cornerRadius")) {
                hasCornerRadius = true;
                hasCornerRadii = !attr.equals("cornerRadius");
                continue;
            }
            layoutRule = NO_LAYOUT_RULE;
            boolean layoutTarget = false;
            switch (attr) {
                case "id":
                    String idValue = parseId(entry.getValue());
                    if (parent != null) {
                        DynamicLayoutInfo info = getDynamicLayoutInfo(parent);
                        int newId = highestIdNumberUsed++;
                        view.setId(newId);
                        info.nameToIdNumber.put(idValue, newId);
                    }
                    break;
                case "width":
                case "layout_width":
                    switch (entry.getValue()) {
                        case "wrap_content":
                            layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                            break;
                        case "fill_parent":
                        case "match_parent":
                            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
                            break;
                        default:
                            layoutParams.width = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, true);
                            break;
                    }
                    break;
                case "height":
                case "layout_height":
                    switch (entry.getValue()) {
                        case "wrap_content":
                            layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                            break;
                        case "fill_parent":
                        case "match_parent":
                            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
                            break;
                        default:
                            layoutParams.height = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, false);
                            break;
                    }
                    break;
                case "layout_gravity":
                    if (parent != null && parent instanceof LinearLayout) {
                        ((LinearLayout.LayoutParams) layoutParams).gravity = parseGravity(entry.getValue());
                    } else if (parent != null && parent instanceof FrameLayout) {
                        ((FrameLayout.LayoutParams) layoutParams).gravity = parseGravity(entry.getValue());
                    }
                    break;
                case "layout_weight":
                    if (parent != null && parent instanceof LinearLayout) {
                        ((LinearLayout.LayoutParams) layoutParams).weight = Float.parseFloat(entry.getValue());
                    }
                    break;
                case "layout_below":
                    layoutRule = RelativeLayout.BELOW;
                    layoutTarget = true;
                    break;
                case "layout_above":
                    layoutRule = RelativeLayout.ABOVE;
                    layoutTarget = true;
                    break;
                case "layout_toLeftOf":
                    layoutRule = RelativeLayout.LEFT_OF;
                    layoutTarget = true;
                    break;
                case "layout_toRightOf":
                    layoutRule = RelativeLayout.RIGHT_OF;
                    layoutTarget = true;
                    break;
                case "layout_alignBottom":
                    layoutRule = RelativeLayout.ALIGN_BOTTOM;
                    layoutTarget = true;
                    break;
                case "layout_alignTop":
                    layoutRule = RelativeLayout.ALIGN_TOP;
                    layoutTarget = true;
                    break;
                case "layout_alignLeft":
                case "layout_alignStart":
                    layoutRule = RelativeLayout.ALIGN_LEFT;
                    layoutTarget = true;
                    break;
                case "layout_alignRight":
                case "layout_alignEnd":
                    layoutRule = RelativeLayout.ALIGN_RIGHT;
                    layoutTarget = true;
                    break;
                case "layout_alignParentBottom":
                    layoutRule = RelativeLayout.ALIGN_PARENT_BOTTOM;
                    break;
                case "layout_alignParentTop":
                    layoutRule = RelativeLayout.ALIGN_PARENT_TOP;
                    break;
                case "layout_alignParentLeft":
                case "layout_alignParentStart":
                    layoutRule = RelativeLayout.ALIGN_PARENT_LEFT;
                    break;
                case "layout_alignParentRight":
                case "layout_alignParentEnd":
                    layoutRule = RelativeLayout.ALIGN_PARENT_RIGHT;
                    break;
                case "layout_centerHorizontal":
                    layoutRule = RelativeLayout.CENTER_HORIZONTAL;
                    break;
                case "layout_centerVertical":
                    layoutRule = RelativeLayout.CENTER_VERTICAL;
                    break;
                case "layout_centerInParent":
                    layoutRule = RelativeLayout.CENTER_IN_PARENT;
                    break;
                case "layout_margin":
                    marginLeft = marginRight = marginTop = marginBottom = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;
                case "layout_marginLeft":
                    marginLeft = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, true);
                    break;
                case "layout_marginTop":
                    marginTop = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, false);
                    break;
                case "layout_marginRight":
                    marginRight = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, true);
                    break;
                case "layout_marginBottom":
                    marginBottom = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics(), parent, false);
                    break;
                case "padding":
                    paddingBottom = paddingLeft = paddingRight = paddingTop = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;
                case "paddingLeft":
                    paddingLeft = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;
                case "paddingTop":
                    paddingTop = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;
                case "paddingRight":
                    paddingRight = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;
                case "paddingBottom":
                    paddingBottom = DimensionConverter.stringToDimensionPixelSize(entry.getValue(), view.getResources().getDisplayMetrics());
                    break;

            }
            if (layoutRule != NO_LAYOUT_RULE && parent instanceof RelativeLayout) {
                if (layoutTarget) {
                    int anchor = idNumFromIdString(parent, parseId(entry.getValue()));
                    ((RelativeLayout.LayoutParams) layoutParams).addRule(layoutRule, anchor);
                } else if (entry.getValue().equals("true")) {
                    ((RelativeLayout.LayoutParams) layoutParams).addRule(layoutRule);
                }
            }
        }
        // TODO: this is a giant mess; come up with a simpler way of deciding what to draw for the background
        if (attrs.containsKey("background") || attrs.containsKey("borderColor")) {
            String bgValue = attrs.containsKey("background") ? attrs.get("background") : null;
            if (bgValue != null && bgValue.startsWith("@drawable/")) {
                view.setBackground(getDrawableByName(view, bgValue));
            } else if (bgValue == null || bgValue.startsWith("#") || bgValue.startsWith("@color")) {
                if (view instanceof Button || attrs.containsKey("pressedColor")) {
                    int bgColor = parseColor(view, bgValue == null ? "#00000000" : bgValue);
                    int pressedColor;
                    if (attrs.containsKey("pressedColor")) {
                        pressedColor = parseColor(view, attrs.get("pressedColor"));
                    } else {
                        pressedColor = adjustBrightness(bgColor, 0.9f);
                    }
                    GradientDrawable gd = new GradientDrawable();
                    gd.setColor(bgColor);
                    GradientDrawable pressedGd = new GradientDrawable();
                    pressedGd.setColor(pressedColor);
                    if (hasCornerRadii) {
                        float radii[] = new float[8];
                        for (int i = 0; i < CORNERS.length; i++) {
                            String corner = CORNERS[i];
                            if (attrs.containsKey("cornerRadius" + corner)) {
                                radii[i * 2] = radii[i * 2 + 1] = DimensionConverter.stringToDimension(attrs.get("cornerRadius" + corner), view.getResources().getDisplayMetrics());
                            }
                            gd.setCornerRadii(radii);
                            pressedGd.setCornerRadii(radii);
                        }
                    } else if (hasCornerRadius) {
                        float cornerRadius = DimensionConverter.stringToDimension(attrs.get("cornerRadius"), view.getResources().getDisplayMetrics());
                        gd.setCornerRadius(cornerRadius);
                        pressedGd.setCornerRadius(cornerRadius);
                    }
                    if (attrs.containsKey("borderColor")) {
                        String borderWidth = "1dp";
                        if (attrs.containsKey("borderWidth")) {
                            borderWidth = attrs.get("borderWidth");
                        }
                        int borderWidthPx = DimensionConverter.stringToDimensionPixelSize(borderWidth, view.getResources().getDisplayMetrics());
                        gd.setStroke(borderWidthPx, parseColor(view, attrs.get("borderColor")));
                        pressedGd.setStroke(borderWidthPx, parseColor(view, attrs.get("borderColor")));
                    }
                    StateListDrawable selector = new StateListDrawable();
                    selector.addState(new int[]{android.R.attr.state_pressed}, pressedGd);
                    selector.addState(new int[]{}, gd);
                    view.setBackground(selector);
                    getDynamicLayoutInfo(view).bgDrawable = gd;
                } else if (hasCornerRadius || attrs.containsKey("borderColor")) {
                    GradientDrawable gd = new GradientDrawable();
                    int bgColor = parseColor(view, bgValue == null ? "#00000000" : bgValue);
                    gd.setColor(bgColor);
                    if (hasCornerRadii) {
                        float radii[] = new float[8];
                        for (int i = 0; i < CORNERS.length; i++) {
                            String corner = CORNERS[i];
                            if (attrs.containsKey("cornerRadius" + corner)) {
                                radii[i * 2] = radii[i * 2 + 1] = DimensionConverter.stringToDimension(attrs.get("cornerRadius" + corner), view.getResources().getDisplayMetrics());
                            }
                            gd.setCornerRadii(radii);
                        }
                    } else if (hasCornerRadius) {
                        float cornerRadius = DimensionConverter.stringToDimension(attrs.get("cornerRadius"), view.getResources().getDisplayMetrics());
                        gd.setCornerRadius(cornerRadius);
                    }
                    if (attrs.containsKey("borderColor")) {
                        String borderWidth = "1dp";
                        if (attrs.containsKey("borderWidth")) {
                            borderWidth = attrs.get("borderWidth");
                        }
                        gd.setStroke(DimensionConverter.stringToDimensionPixelSize(borderWidth, view.getResources().getDisplayMetrics()),
                                parseColor(view, attrs.get("borderColor")));
                    }
                    view.setBackground(gd);
                    getDynamicLayoutInfo(view).bgDrawable = gd;
                } else {
                    view.setBackgroundColor(parseColor(view, bgValue));
                }
            }
        }

        if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
            ((ViewGroup.MarginLayoutParams) layoutParams).setMargins(marginLeft, marginTop, marginRight, marginBottom);
        }
        view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
        view.setLayoutParams(layoutParams);
    }

    private static DynamicLayoutInfo getDynamicLayoutInfo(View parent) {
        DynamicLayoutInfo info;
        if (parent.getTag() != null && parent.getTag() instanceof DynamicLayoutInfo) {
            info = (DynamicLayoutInfo) parent.getTag();
        } else {
            info = new DynamicLayoutInfo();
            parent.setTag(info);
        }
        return info;
    }

    private static View.OnClickListener getClickListener(final ViewGroup myParent, final String methodName) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ViewGroup root = myParent;
                DynamicLayoutInfo info = null;
                while (root != null && (root.getParent() instanceof ViewGroup)) {
                    if (root.getTag() != null && root.getTag() instanceof DynamicLayoutInfo) {
                        info = (DynamicLayoutInfo) root.getTag();
                        if (info.delegate != null) break;
                    }
                    root = (ViewGroup) root.getParent();
                }
                if (info != null && info.delegate != null) {
                    final Object delegate = info.delegate;
                    invokeMethod(delegate, methodName, false, view);
                } else {
                    Log.e("DynamicLayoutInflator", "Unable to find valid delegate for click named " + methodName);
                }
            }

            private void invokeMethod(Object delegate, final String methodName, boolean withView, View view) {
                Object[] args = null;
                String finalMethod = methodName;
                if (methodName.endsWith(")")) {
                    String[] parts = methodName.split("[(]", 2);
                    finalMethod = parts[0];
                    try {
                        String argText = parts[1].replace("&quot;", "\"");
                        JSONArray arr = new JSONArray("[" + argText.substring(0, argText.length() - 1) + "]");
                        args = new Object[arr.length()];
                        for (int i = 0; i < arr.length(); i++) {
                            args[i] = arr.get(i);
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                } else if (withView) {
                    args = new Object[1];
                    args[0] = view;
                }
                Class<?> klass = delegate.getClass();
                try {

                    Class<?>[] argClasses = null;
                    if (args != null && args.length > 0) {
                        argClasses = new Class[args.length];
                        if (withView) {
                            argClasses[0] = View.class;
                        } else {
                            for (int i = 0; i < args.length; i++) {
                                Class<?> argClass = args[i].getClass();
                                if (argClass == Integer.class)
                                    argClass = int.class; // Nobody uses Integer...
                                argClasses[i] = argClass;
                            }
                        }
                    }
                    Method method = klass.getMethod(finalMethod, argClasses);
                    method.invoke(delegate, args);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                    if (!withView && !methodName.endsWith(")")) {
                        invokeMethod(delegate, methodName, true, view);
                    }
                }
            }
        };
    }

    private static String parseId(String value) {
        if (value.startsWith("@+id/")) {
            return value.substring(5);
        } else if (value.startsWith("@id/")) {
            return value.substring(4);
        }
        return value;
    }

    private static int parseGravity(String value) {
        int gravity = Gravity.NO_GRAVITY;
        String[] parts = value.toLowerCase().split("[|]");
        for (String part : parts) {
            switch (part) {
                case "center":
                    gravity = gravity | Gravity.CENTER;
                    break;
                case "left":
                case "textStart":
                    gravity = gravity | Gravity.LEFT;
                    break;
                case "right":
                case "textEnd":
                    gravity = gravity | Gravity.RIGHT;
                    break;
                case "top":
                    gravity = gravity | Gravity.TOP;
                    break;
                case "bottom":
                    gravity = gravity | Gravity.BOTTOM;
                    break;
                case "center_horizontal":
                    gravity = gravity | Gravity.CENTER_HORIZONTAL;
                    break;
                case "center_vertical":
                    gravity = gravity | Gravity.CENTER_VERTICAL;
                    break;
            }
        }
        return gravity;
    }

    public static int idNumFromIdString(View view, String id) {
        if (!(view instanceof ViewGroup)) return 0;
        Object tag = view.getTag();
        if (!(tag instanceof DynamicLayoutInfo)) return 0; // not inflated by this class
        DynamicLayoutInfo info = (DynamicLayoutInfo) view.getTag();
        if (!info.nameToIdNumber.containsKey(id)) {
            ViewGroup grp = (ViewGroup) view;
            for (int i = 0; i < grp.getChildCount(); i++) {
                int val = idNumFromIdString(grp.getChildAt(i), id);
                if (val != 0) return val;
            }
            return 0;
        }
        return info.nameToIdNumber.get(id);
    }

    @Nullable
    public static View findViewByIdString(View view, String id) {
        int idNum = idNumFromIdString(view, id);
        if (idNum == 0) return null;
        return view.findViewById(idNum);
    }

    public static int parseColor(View view, String text) {
        if (text.startsWith("@color/")) {
            Resources resources = view.getResources();
            return resources.getColor(resources.getIdentifier(text.substring("@color/".length()), "color", view.getContext().getPackageName()));
        }
        if (text.length() == 4 && text.startsWith("#")) {
            text = "#" + text.charAt(1) + text.charAt(1) + text.charAt(2) + text.charAt(2) + text.charAt(3) + text.charAt(3);
        }
        return Color.parseColor(text);
    }

    public static int adjustBrightness(int color, float amount) {
        int red = color & 0xFF0000 >> 16;
        int green = color & 0x00FF00 >> 8;
        int blue = color & 0x0000FF;
        int result = (int) (blue * amount);
        result += (int) (green * amount) << 8;
        result += (int) (red * amount) << 16;
        return result;
    }

    public static Drawable getDrawableByName(View view, String name) {
        Resources resources = view.getResources();
        return resources.getDrawable(resources.getIdentifier(name, "drawable",
                view.getContext().getPackageName()));
    }

    public static void createViewRunnables() {
        viewRunnables = new HashMap<>(30);
        viewRunnables.put("scaleType", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof ImageView) {
                    ImageView.ScaleType scaleType = ((ImageView) view).getScaleType();
                    switch (value.toLowerCase()) {
                        case "center":
                            scaleType = ImageView.ScaleType.CENTER;
                            break;
                        case "center_crop":
                            scaleType = ImageView.ScaleType.CENTER_CROP;
                            break;
                        case "center_inside":
                            scaleType = ImageView.ScaleType.CENTER_INSIDE;
                            break;
                        case "fit_center":
                            scaleType = ImageView.ScaleType.FIT_CENTER;
                            break;
                        case "fit_end":
                            scaleType = ImageView.ScaleType.FIT_END;
                            break;
                        case "fit_start":
                            scaleType = ImageView.ScaleType.FIT_START;
                            break;
                        case "fit_xy":
                            scaleType = ImageView.ScaleType.FIT_XY;
                            break;
                        case "matrix":
                            scaleType = ImageView.ScaleType.MATRIX;
                            break;
                    }
                    ((ImageView) view).setScaleType(scaleType);
                }
            }
        });
        viewRunnables.put("orientation", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof LinearLayout) {
                    ((LinearLayout) view).setOrientation(value.equals("vertical") ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
                }
            }
        });
        viewRunnables.put("text", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    ((TextView) view).setText(value);
                }
            }
        });
        viewRunnables.put("textSize", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    ((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_PX, DimensionConverter.stringToDimension(value, view.getResources().getDisplayMetrics()));
                }
            }
        });
        viewRunnables.put("textColor", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    ((TextView) view).setTextColor(parseColor(view, value));
                }
            }
        });
        viewRunnables.put("textStyle", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    int typeFace = Typeface.NORMAL;
                    if (value.contains("bold")) typeFace |= Typeface.BOLD;
                    else if (value.contains("italic")) typeFace |= Typeface.ITALIC;
                    ((TextView) view).setTypeface(null, typeFace);
                }
            }
        });
        viewRunnables.put("textAlignment", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
                    int alignment = View.TEXT_ALIGNMENT_TEXT_START;
                    switch (value) {
                        case "center":
                            alignment = View.TEXT_ALIGNMENT_CENTER;
                            break;
                        case "left":
                        case "textStart":
                            break;
                        case "right":
                        case "textEnd":
                            alignment = View.TEXT_ALIGNMENT_TEXT_END;
                            break;
                    }
                    view.setTextAlignment(alignment);
                } else {
                    int gravity = Gravity.LEFT;
                    switch (value) {
                        case "center":
                            gravity = Gravity.CENTER;
                            break;
                        case "left":
                        case "textStart":
                            break;
                        case "right":
                        case "textEnd":
                            gravity = Gravity.RIGHT;
                            break;
                    }
                    ((TextView) view).setGravity(gravity);
                }
            }
        });
        viewRunnables.put("ellipsize", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    TextUtils.TruncateAt where = TextUtils.TruncateAt.END;
                    switch (value) {
                        case "start":
                            where = TextUtils.TruncateAt.START;
                            break;
                        case "middle":
                            where = TextUtils.TruncateAt.MIDDLE;
                            break;
                        case "marquee":
                            where = TextUtils.TruncateAt.MARQUEE;
                            break;
                        case "end":
                            break;
                    }
                    ((TextView) view).setEllipsize(where);
                }
            }
        });
        viewRunnables.put("singleLine", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    ((TextView) view).setSingleLine();
                }
            }
        });
        viewRunnables.put("hint", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof EditText) {
                    ((EditText) view).setHint(value);
                }
            }
        });
        viewRunnables.put("inputType", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof TextView) {
                    int inputType = 0;
                    switch (value) {
                        case "textEmailAddress":
                            inputType |= InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
                            break;
                        case "number":
                            inputType |= InputType.TYPE_CLASS_NUMBER;
                            break;
                        case "phone":
                            inputType |= InputType.TYPE_CLASS_PHONE;
                            break;
                    }
                    if (inputType > 0) ((TextView) view).setInputType(inputType);
                }
            }
        });
        viewRunnables.put("gravity", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                int gravity = parseGravity(value);
                if (view instanceof TextView) {
                    ((TextView) view).setGravity(gravity);
                } else if (view instanceof LinearLayout) {
                    ((LinearLayout) view).setGravity(gravity);
                } else if (view instanceof RelativeLayout) {
                    ((RelativeLayout) view).setGravity(gravity);
                }
            }
        });
        viewRunnables.put("src", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                if (view instanceof ImageView) {
                    String imageName = value;
                    if (imageName.startsWith("//")) imageName = "http:" + imageName;
                    if (imageName.startsWith("http")) {
                        if (imageLoader != null) {
                            if (attrs.containsKey("cornerRadius")) {
                                int radius = DimensionConverter.stringToDimensionPixelSize(attrs.get("cornerRadius"), view.getResources().getDisplayMetrics());
                                imageLoader.loadRoundedImage((ImageView) view, imageName, radius);
                            } else {
                                imageLoader.loadImage((ImageView) view, imageName);
                            }
                        }
                    } else if (imageName.startsWith("@drawable/")) {
                        imageName = imageName.substring("@drawable/".length());
                        ((ImageView) view).setImageDrawable(getDrawableByName(view, imageName));
                    }
                }
            }
        });
        viewRunnables.put("visibility", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                int visibility = View.VISIBLE;
                String visValue = value.toLowerCase();
                if (visValue.equals("gone")) visibility = View.GONE;
                else if (visValue.equals("invisible")) visibility = View.INVISIBLE;
                view.setVisibility(visibility);
            }
        });
        viewRunnables.put("clickable", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                view.setClickable(value.equals("true"));
            }
        });
        viewRunnables.put("tag", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                // Sigh, this is dangerous because we use tags for other purposes
                if (view.getTag() == null) view.setTag(value);
            }
        });
        viewRunnables.put("onClick", new ViewParamRunnable() {
            @Override
            public void apply(View view, String value, ViewGroup parent, Map<String, String> attrs) {
                view.setOnClickListener(getClickListener(parent, value));
            }
        });
    }

    public interface ViewParamRunnable {
        void apply(View view, String value, ViewGroup parent, Map<String, String> attrs);
    }

    public interface ImageLoader {
        void loadImage(ImageView view, String url);

        void loadRoundedImage(ImageView view, String url, int radius);
    }

    public static class DynamicLayoutInfo {
        public HashMap<String, Integer> nameToIdNumber;
        public Object delegate;
        public GradientDrawable bgDrawable;
        public DynamicLayoutInfo() {
            nameToIdNumber = new HashMap<>();
        }
    }

}
<file_sep>/common/src/main/java/com/jecelyin/common/utils/StringUtils.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.common.utils;

import android.text.TextUtils;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * String工具类
 */
public class StringUtils {

    public static boolean isEmpty(String input) {
        return TextUtils.isEmpty(input);
    }

    /**
     * 不能使用 {@link MessageDigest#isEqual} 因为像华为emui 2.3这个函数不能正确比较
     * @param digesta
     * @param digestb
     * @return
     */
    public static boolean isEqual(byte[] digesta, byte[] digestb) {
        if (digesta.length != digestb.length) {
            return false;
        }
        // Perform a constant time comparison to avoid timing attacks.
        int v = 0;
        for (int i = 0; i < digesta.length; i++) {
            v |= (digesta[i] ^ digestb[i]);
        }
        return v == 0;
    }

    /**
     * 检测变量的值是否为一个整型数据；
     */
    public final static boolean isInt(String value) {
        if (isEmpty(value))
            return false;

        try {
            Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return false;
        }

        return true;
    }

    /**
     * 判断变量的值是否为double类型
     */
    public final static boolean isDouble(String value) {
        if (isEmpty(value))
            return false;
        try {
            Double.parseDouble(value);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }
    /**
     * 解析一个字符串为整数；
     */
    public final static int toInt(String value) {
        return toInt(value, 0);
    }

    public final static int toInt(String value, int defaultValue) {
        try {
            return Integer.parseInt(value);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 解析一个字符串为double
     */
    public final static double toDouble(String value) {
        return toDouble(value, 0);
    }

    public final static double toDouble(String value, double defaultValue) {
        if (isDouble(value))
            return Double.parseDouble(value);
        return defaultValue;
    }

    /**
     * 解析一个字符串为float
     */
    public static float toFloat(String value) {
        return toFloat(value, 0);
    }

    public static float toFloat(String value, float defaultValue) {
        try {
            return Float.parseFloat(value);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 对象转整数
     *
     * @param obj
     * @return 转换异常返回 0
     */
    public static long toLong(String obj) {
        try {
            return Long.parseLong(obj);
        } catch (Exception e) {
        }
        return 0;
    }

    /**
     * 字符串转布尔值
     *
     * @param b
     * @return 转换异常返回 false
     */
    public static boolean toBool(String b) {
        try {
            return Boolean.parseBoolean(b);
        } catch (Exception e) {
        }
        return false;
    }

    /**
     * 判定输入汉字
     * @param c
     * @return
     */
    public  final static boolean isChinese(char c) {
        Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
        if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
            return true;
        }
        return false;
    }

    /**
     * Returns a string containing the tokens joined by delimiters.
     * @param tokens an array objects to be joined.
     */
    public static String join(CharSequence delimiter, int[] tokens) {
        StringBuilder sb = new StringBuilder();
        boolean firstTime = true;
        for (Object token: tokens) {
            if (firstTime) {
                firstTime = false;
            } else {
                sb.append(delimiter);
            }
            sb.append(token);
        }
        return sb.toString();
    }

    public static String urlencode(String s) {
        try {
            s = URLEncoder.encode(s, "UTF-8");
        } catch (Exception e) {
        }
        return s;
    }

    public static String urldecode(String s) {
        try {
            s = URLDecoder.decode(s, "UTF-8");
        } catch (Exception e) {
        }
        return s;
    }

    public static String md5(String s) {
        try {
            // Create MD5 Hash
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            digest.update(s.getBytes());
            byte messageDigest[] = digest.digest();

            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < messageDigest.length; i++)
                hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String formatSize(long size, int decimalPoints) {
        return formatSize(size, decimalPoints, true);
    }

    public static String formatSize(long size) {
        return formatSize(size, true);
    }

    public static String formatSize(long size, boolean includeUnits) {
        return formatSize(size, 2, includeUnits);
    }

    public static String formatSize(long size, int decimalPoints, boolean includeUnits) {

        int kb = 1024;
        int mb = kb * kb;
        int gb = mb * kb;

        if(size < 0) return "";

        int factor = (10 ^ decimalPoints);

        String ssize = "";

        if (size <= kb)
            ssize = size + " B";
        else if (size > kb && size <= mb)
            ssize = ((double)Math.round(((double)size / kb) * factor) / factor)
                    + (includeUnits ? " KB" : "");
        else if (size > mb && size <= gb)
            ssize = ((double)Math.round(((double)size / mb) * factor) / factor)
                    + (includeUnits ? " MB" : "");
        else if (size > gb)
            ssize = ((double)Math.round(((double)size / gb) * factor) / factor)
                    + (includeUnits ? " GB" : "");

        return ssize;
    }

}
<file_sep>/app/src/main/java/com/duy/ide/command/Command.java
package com.duy.ide.command;

/**
 * Created by Duy on 20-Dec-17.
 */

public class Command implements ICommand{

    @Override
    public boolean execute(Object... params) {
        return false;
    }
}
<file_sep>/fileexplorer/src/main/java/com/jecelyin/android/file_explorer/adapter/FileListItemAdapter.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.android.file_explorer.adapter;

import android.content.res.Resources;
import android.databinding.DataBindingUtil;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.jecelyin.android.file_explorer.R;
import com.jecelyin.android.file_explorer.databinding.FileListItemBinding;
import com.jecelyin.android.file_explorer.io.JecFile;
import com.jecelyin.android.file_explorer.model.FileItemModel;
import com.jecelyin.android.file_explorer.util.MimeTypes;
import com.jecelyin.android.file_explorer.util.OnCheckedChangeListener;
import com.jecelyin.common.adapter.BindingViewHolder;
import com.jecelyin.common.listeners.OnItemClickListener;
import com.jecelyin.common.utils.StringUtils;
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;

import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
 * @author <NAME> <<EMAIL>>
 */
public class FileListItemAdapter extends RecyclerView.Adapter<BindingViewHolder<FileListItemBinding>> implements FastScrollRecyclerView.SectionedAdapter {
    private JecFile[] data;
    private final String year;
    private final SparseIntArray checkedArray;
    private OnCheckedChangeListener onCheckedChangeListener;
    private OnItemClickListener onItemClickListener;
    private JecFile[] mOriginalValues;
    private int itemCount;

    public FileListItemAdapter() {
//        year = String.valueOf(new Date().getYear());
        year = String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
        checkedArray = new SparseIntArray();
    }

    public void setData(JecFile[] data) {
        this.data = data;
        itemCount = data.length;
        mOriginalValues = data.clone();
        notifyDataSetChanged();
    }

    public void filter(CharSequence filterText) {
        if (mOriginalValues == null)
            return;

        if (TextUtils.isEmpty(filterText)) {
            data = mOriginalValues;
            itemCount = mOriginalValues.length;
            notifyDataSetChanged();
            return;
        }

        data = new JecFile[mOriginalValues.length];

        filterText = filterText.toString().toLowerCase();
        int index = 0;
        for(JecFile path : mOriginalValues) {
            if (path.getName().toLowerCase().contains(filterText)) {
                data[index++] = path;
            }
        }
        itemCount = index;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public String getSectionName(int position) {
        JecFile file = getItem(position);
        char c = file.getName().charAt(0);

        if ( (c >= '0' && c <= '9')
            || (c >= 'a' && c <= 'z')
            || (c >= 'A' && c <= 'Z')
                ) {
            return String.valueOf(c);
        }

        return "#";
    }

    public JecFile getItem(int position) {
        return data[position];
    }

    public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
        this.onCheckedChangeListener = onCheckedChangeListener;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    @Override
    public int getItemCount() {
        return itemCount;
    }

    @Override
    public BindingViewHolder<FileListItemBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
        FileListItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.file_list_item, parent, false);

        return new BindingViewHolder<>(binding);
    }

    @Override
    public void onBindViewHolder(final BindingViewHolder<FileListItemBinding> holder, final int position) {
        JecFile path = data[position];

        MimeTypes mimeTypes = MimeTypes.getInstance();
        Resources res = holder.itemView.getResources();
        int color, icon;
        if (path.isDirectory()) {
            color = R.color.type_folder;
            icon = R.drawable.file_type_folder;

        } else if (mimeTypes.isImageFile(path)) {
            color = R.color.type_media;
            icon = R.drawable.file_type_image;

        } else if (mimeTypes.isVideoFile(path)) {
            color = R.color.type_media;
            icon = R.drawable.file_type_video;

        } else if (mimeTypes.isAudioFile(path)) {
            color = R.color.type_media;
            icon = R.drawable.file_type_audio;

        } else if (mimeTypes.isAPKFile(path)) {
            color = R.color.type_apk;
            icon = R.drawable.file_type_apk;

        } else if (mimeTypes.isArchive(path)) {
            color = R.color.type_archive;
            icon = R.drawable.file_type_archive;

        } else if (mimeTypes.isCodeFile(path)) {
            color = R.color.type_code;
            icon = R.drawable.file_type_code;

        } else if (mimeTypes.isTextFile(path)) {
            color = R.color.type_text;
            icon = R.drawable.file_type_text;

        } else {
            color = R.color.type_file;
            icon = TextUtils.isEmpty(path.getExtension()) ? R.drawable.file_type_file : 0;
        }

        final FileListItemBinding binding = holder.getBinding();
        binding.iconImageView.setDefaultImageResource(icon);
        binding.iconImageView.setDefaultBackgroundColor(res.getColor(color));

        FileItemModel item = new FileItemModel();
        item.setName(path.getName());
        item.setExt(icon == 0 && color == R.color.type_file ? path.getExtension() : "");
        item.setDate(getDate(path.lastModified()));
        item.setSecondLine(path.isFile() ? StringUtils.formatSize(path.length()) : "");
        binding.setItem(item);

        binding.iconImageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toggleChecked(position, binding);
            }
        });
        holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                toggleChecked(position, binding);
                return true;
            }
        });
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (checkedArray != null && checkedArray.size() > 0) {
                    toggleChecked(position, binding);
                    return;
                }
                if (onItemClickListener != null)
                    onItemClickListener.onItemClick(position, v);
            }
        });

        boolean isChecked = isChecked(position);
        setViewCheckedStatus(isChecked, binding);
    }

    private void setViewCheckedStatus(boolean isChecked, FileListItemBinding binding) {
        binding.iconImageView.setChecked(isChecked);

        if(!isChecked) {
            binding.getRoot().setSelected(false);
            binding.extTextView.setVisibility(View.VISIBLE);
        } else {
            binding.getRoot().setSelected(true);
            binding.extTextView.setVisibility(View.INVISIBLE);
        }
    }

    private void toggleChecked(int position, FileListItemBinding binding) {
        boolean isChecked = isChecked(position);
        if(isChecked) {
            checkedArray.delete(position);
        } else {
            checkedArray.put(position, 1);
        }

        setViewCheckedStatus(!isChecked, binding);

        if(onCheckedChangeListener != null) {
            onCheckedChangeListener.onCheckedChanged(getItem(position), position, !isChecked);
            onCheckedChangeListener.onCheckedChanged(checkedArray.size());
        }

    }

    public boolean isChecked(int position) {
        return checkedArray.get(position) == 1;
    }

    private String getDate(long f) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String date = (sdf.format(f));
        if (date.substring(0, 4).equals(year))
            date = date.substring(5);
        return date;
    }

    public void checkAll(boolean checked) {
        if (checked) {
            int count = getItemCount();
            for (int i = 0; i < count; i++) {
                checkedArray.put(i, 1);
            }
        } else {
            checkedArray.clear();
        }

        if (onCheckedChangeListener != null) {
            onCheckedChangeListener.onCheckedChanged(checkedArray.size());
        }

        notifyDataSetChanged();
    }

}
<file_sep>/app/src/main/java/com/duy/project/view/dialog/DialogNewFolder.java
package com.duy.project.view.dialog;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;

import com.duy.ide.R;
import com.duy.project.ProjectFileContract;
import com.duy.project.file.java.JavaProjectFolder;

import java.io.File;

/**
 * Created by Duy on 20-Dec-17.
 */

public class DialogNewFolder extends AppCompatDialogFragment implements View.OnClickListener {
    public static final String TAG = "DialogNewFolder";

    private static final String KEY_PROJECT_FILE = "project_file";
    private static final String KEY_PARENT_FILE = "parent_file";
    private EditText mEditName;
    @Nullable
    private ProjectFileContract.FileActionListener listener;

    public static DialogNewFolder newInstance(@NonNull JavaProjectFolder p, @Nullable File currentFolder) {
        Bundle args = new Bundle();
        args.putSerializable(KEY_PROJECT_FILE, p);
        args.putSerializable(KEY_PARENT_FILE, currentFolder);
        DialogNewFolder fragment = new DialogNewFolder();
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.dialog_new_folder, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null && dialog.getWindow() != null) {
            Window window = dialog.getWindow();
            window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (ProjectFileContract.FileActionListener) getActivity();
        } catch (ClassCastException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_cancel:
                this.dismiss();
                break;
            case R.id.btn_create:
                createNewFolder();
                break;
        }
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mEditName = view.findViewById(R.id.edit_class_name);

        view.findViewById(R.id.btn_create).setOnClickListener(this);
        view.findViewById(R.id.btn_cancel).setOnClickListener(this);
    }


    private void createNewFolder() {
        String fileName = mEditName.getText().toString();
        if (fileName.isEmpty()) {
            mEditName.setError(getString(R.string.enter_name));
            return;
        }
        try {
            File parent = (File) getArguments().getSerializable(KEY_PARENT_FILE);
            File folder = new File(parent, fileName);
            if (!parent.exists()) {
                parent.mkdirs();
            }
            folder.mkdir();
            listener.onNewFileCreated(folder);
            dismiss();
        } catch (Exception e) {
            Toast.makeText(getContext(), "Can not create new file", Toast.LENGTH_SHORT).show();
        }
    }
}
<file_sep>/styles/src/main/java/com/jecelyin/editor/v2/FullScreenActivity.java
/*
 * Copyright (C) 2016 <NAME> <<EMAIL>>
 *
 * This file is part of 920 Text Editor.
 *
 * 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.jecelyin.editor.v2;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.WindowManager;

import com.jecelyin.common.app.JecActivity;

/**
 * @author <NAME> <<EMAIL>>
 */
public class FullScreenActivity extends JecActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int theme = Pref.getInstance(this).getTheme();
        if (theme != 0) {
            setTheme(Pref.THEMES[theme]);
        }

        if (isFullScreenMode()) {
            enabledFullScreenMode();
        }
    }

    @Override
    protected boolean isFullScreenMode() {
        return Pref.getInstance(this).isFullScreenMode();
    }

    private void enabledFullScreenMode() {
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        View decorView = getWindow().getDecorView();
        // Hide the status bar.
        int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
    }
}
<file_sep>/app/src/main/java/com/duy/compile/message/MessageContract.java
package com.duy.compile.message;

import android.support.annotation.WorkerThread;

import com.duy.JavaApplication;

/**
 * Created by duy on 19/07/2017.
 */

public class MessageContract {
    public interface View {
        @WorkerThread
        void append(String text);

        @WorkerThread
        void appendOut(byte[] chars, int start, int end);

        @WorkerThread
        void appendErr(byte[] chars, int start, int end);

        @WorkerThread
        void clear();

        void setPresenter(Presenter presenter);
    }

    public interface Presenter {
        @WorkerThread
        void clear();

        @WorkerThread
        void append(String s);

        void resume(JavaApplication application);

        void pause(JavaApplication application);
    }
}
<file_sep>/app/src/main/java/com/duy/JavaApplication.java
package com.duy;

import android.support.annotation.NonNull;
import android.support.multidex.MultiDexApplication;
import android.support.v7.app.AppCompatDelegate;
import android.util.Log;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;

/**
 * Created by Duy on 17-Jul-17.
 */

public class JavaApplication extends MultiDexApplication {
    private ArrayList<PrintStream> out = new ArrayList<>();
    private ArrayList<PrintStream> err = new ArrayList<>();

    private InterceptorOutputStream systemOut;
    private InterceptorOutputStream systemErr;

    @Override
    public void onCreate() {
        super.onCreate();
        systemOut = new InterceptorOutputStream(System.out, out);
        systemErr = new InterceptorOutputStream(System.err, err);
        System.setOut(systemOut);
        System.setErr(systemErr);

        //for log cat
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    public void addStdOut(PrintStream out) {
        systemOut.add(out);
    }

    public void addStdErr(PrintStream err) {
        systemErr.add(err);
    }

    public void removeOutStream(PrintStream out) {
        systemOut.remove(out);
    }

    public void removeErrStream(PrintStream err) {
        systemErr.remove(err);
    }

    private static class InterceptorOutputStream extends PrintStream {

        private static final String TAG = "InterceptorOutputStream";
        private ArrayList<PrintStream> streams;

        public InterceptorOutputStream(@NonNull OutputStream file, ArrayList<PrintStream> streams) {
            super(file, true);
            this.streams = streams;
        }

        public ArrayList<PrintStream> getStreams() {
            return streams;
        }

        public void setStreams(ArrayList<PrintStream> streams) {
            this.streams = streams;
        }

        public void add(PrintStream out) {
            Log.d(TAG, "add() called with: out = [" + out + "]");

            this.streams.add(out);
        }

        public void remove(PrintStream out) {
            Log.d(TAG, "remove() called with: out = [" + out + "]");

            this.streams.remove(out);
        }

        @Override
        public void write(@NonNull byte[] buf, int off, int len) {
            super.write(buf, off, len);
            if (streams != null) {
                for (PrintStream printStream : streams) {
                    printStream.write(buf, off, len);
                }
            }
        }
    }

}
