public class PromptDialog extends BaseDialogFragment {
    public static final String DEFAULT_FRAGMENT_TAG = PromptDialog.class.getName();

    private static final String BUNDLE_ID = "PromptDialog.id";
    private static final String BUNDLE_TITLE = "PromptDialog.title";
    private static final String BUNDLE_MESSAGE = "PromptDialog.message";
    private static final String BUNDLE_CALLBACK = "PromptDialog.callback";
    private static final String BUNDLE_CANCELABLE = "PromptDialog.cancelable";
    private static final String BUNDLE_SHOW_CANCEL_BTN = "PromptDialog.show_cancel_btn";
    private static final String BUNDLE_BTN_OK_TEXT = "PromptDialog.btn_ok_text";
    private static final String BUNDLE_BTN_CANCEL_TEXT = "PromptDialog.btn_cancel_text";
    private static final String BUNDLE_BUNDLE = "PromptDialog.bundle";

    private Bundle bundle;

    public static PromptDialog show(FragmentManager fm, DialogFragment oldDialog, String fragmentTagOpt, String id, CharSequence title, CharSequence message, boolean callback) {
        return show(fm, oldDialog, fragmentTagOpt, id, title, message, callback, false, false, null);
    }

    public static PromptDialog show(FragmentManager fm, DialogFragment oldDialog, String fragmentTagOpt, String id, CharSequence title, CharSequence message, boolean callback, boolean cancelable, boolean showCancelBtn, Bundle bundle) {
        return show(fm, oldDialog, fragmentTagOpt, id, title, message, callback, cancelable, showCancelBtn, bundle, null, null);
    }

    // btnOkText, btnCancelText - pokud jsou prazdne stringy (ne null!), vubec se nezobrazi
    public static PromptDialog show(FragmentManager fm, DialogFragment oldDialog, String fragmentTagOpt, String id, CharSequence title, CharSequence message, boolean callback, boolean cancelable, boolean showCancelBtn, Bundle bundle, CharSequence btnOkText, CharSequence btnCancelTxt) {
        PromptDialog ret = newInstance(id, title, message, callback, cancelable, showCancelBtn, bundle, btnOkText, btnCancelTxt);
        return FragmentUtils.showDialogRemoveOldOne(fm, oldDialog, ret, fragmentTagOpt == null ? DEFAULT_FRAGMENT_TAG : fragmentTagOpt);
    }

    public static PromptDialog newInstance(String id, CharSequence title, CharSequence message, boolean callback, boolean cancelable, boolean showCancelBtn, Bundle bundle, CharSequence btnOkText, CharSequence btnCancelTxt) {
        Bundle b = new Bundle();
        b.putString(BUNDLE_ID, id);
        b.putCharSequence(BUNDLE_TITLE, title);
        b.putCharSequence(BUNDLE_MESSAGE, message);
        b.putBoolean(BUNDLE_CALLBACK, callback);
        b.putBoolean(BUNDLE_CANCELABLE, cancelable);
        b.putBoolean(BUNDLE_SHOW_CANCEL_BTN, showCancelBtn);
        b.putCharSequence(BUNDLE_BTN_OK_TEXT, btnOkText);
        b.putCharSequence(BUNDLE_BTN_CANCEL_TEXT, btnCancelTxt);
        b.putBundle(BUNDLE_BUNDLE, bundle);

        PromptDialog ret = new PromptDialog();
        ret.setArguments(b);
        ret.setCancelable(cancelable);
        return ret;
    }


    @Override
    protected Builder build(Builder b, Bundle savedInstanceState) {
        Bundle a = getArguments();

        this.bundle = a.getBundle(BUNDLE_BUNDLE);

        final CharSequence title = a.getCharSequence(BUNDLE_TITLE);
        final CharSequence btnOkText = a.getCharSequence(BUNDLE_BTN_OK_TEXT);
        final CharSequence btnCancelText = a.getCharSequence(BUNDLE_BTN_CANCEL_TEXT);

        if (!TextUtils.isEmpty(title)) {
            b.setTitle(title);
        }
        b.setMessage(a.getCharSequence(BUNDLE_MESSAGE));
        if (!"".equals(btnOkText)) {
            b.setPositiveButton(btnOkText != null ? btnOkText : getString(android.R.string.ok), new OnClickListener() {
                @Override
                public void onClick(View v) {
                    onPromptDialogDone(getArguments().getString(BUNDLE_ID), false, bundle);
                    dismiss();
                }
            });
        }

        if (a.getBoolean(BUNDLE_CANCELABLE) && !"".equals(btnCancelText)) {
            b.setNegativeButton(btnCancelText != null ? btnCancelText : getString(android.R.string.cancel), new OnClickListener() {
                @Override
                public void onClick(View v) {
                    onPromptDialogDone(getArguments().getString(BUNDLE_ID), true, bundle);
                    dismiss();
                }
            });
        }
        return b;
    }

    @Override
    public void onStart() {
        super.onStart();
        if (getDialog() != null) {
            View v = getDialog().findViewById(R.id.sdl__message);
            if (v instanceof TextView) {
                ((TextView)v).setMovementMethod(LinkMovementMethod.getInstance()); // rozchozeni klikacich linku v message
            }
        }
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        super.onCancel(dialog);
        onPromptDialogDone(getArguments().getString(BUNDLE_ID), true, bundle);
    }

    protected void onPromptDialogDone(String id, boolean canceled, Bundle bundle) {
        if (getArguments().getBoolean(BUNDLE_CALLBACK)) {
            if (this.getTargetFragment() != null) {
                ((OnPromptDialogDone)this.getTargetFragment()).onPromptDialogDone(id, canceled, bundle);
            }
            else if (this.getParentFragment() != null) {
                ((OnPromptDialogDone)this.getParentFragment()).onPromptDialogDone(id, canceled, bundle);
            }
            else {
                ((OnPromptDialogDone)this.getActivity()).onPromptDialogDone(id, canceled, bundle);
            }
        }
    }

    public interface OnPromptDialogDone {
        void onPromptDialogDone(String id, boolean canceled, Bundle bundle);
    }
} public static abstract class BaseLocalReceiver extends BaseLocalReceiverProt {
    public BaseLocalReceiver(String action) {
        super(action);
    }

    public BaseLocalReceiver(IntentFilter intentFilter) {
        super(intentFilter);
    }

    @Override
    public boolean register(Context context) {
        return super.register(context);
    }
} public class DialogsFragment extends BaseRetainFragment {
    private static final String TAG = DialogsFragment.class.getSimpleName();

    protected static final String FRAGMENT_TAG = DialogsFragment.class.getName();
    protected static final String DIALOG_FRAGMENT_TAG = FRAGMENT_TAG + ".Dialog";

    protected DialogFragment dialog;
    private String currCallerFragmentTag;
    private boolean wasShownDialogToFinish = false; // po nastaveni na true uz nepovolim zobrazeni zadneho dalsiho dialogu! - ceka se, dokud uzivatel v danem dialogu neklikne na OK, cimz dojde k ukonceni aktivity

    public interface IDialogsFragmentActivity {
        DialogsFragment getDialogsFragment();
    }

    public static <T extends FragmentActivity & IDialogsFragmentActivity> DialogsFragment getInstance(T activity) {
        FragmentManager fm = activity.getSupportFragmentManager();
        DialogsFragment f = (DialogsFragment)fm.findFragmentByTag(FRAGMENT_TAG);
        if (f == null) {
            f = new DialogsFragment();
            fm.beginTransaction().add(f, FRAGMENT_TAG).commit();
        }
        return f;
    }

    //
    // Livecycle callbacks
    //

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        this.dialog = (DialogFragment)getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG);
    }


    //
    // Dialogs
    //

    public boolean getWasShownDialogToFinish() {
        return this.wasShownDialogToFinish;
    }

    public void showErrorMsg(final ITaskContext context, final ITaskResult res, final boolean finish) {
        LogUtils.e(TAG, "showErrorMsg(1): " + res.getError().getMsg(context));

        if (!canShowNewDialog())
            return;

        if (!isReadyToCommitFragments()) {
            addPendingTask(new Runnable() { public void run() { showErrorMsg(context, res, finish); } });
        }
        else {
            wasShownDialogToFinish = finish;
            DialogFragment newDialog = res.getError() instanceof ITaskErrorWtDialog ?
                    ((ITaskErrorWtDialog)res.getError()).createDialog(context, finish)
                    : ErrorMsgDialog.newInstance(res.getError().getMsg(context), finish);

            this.dialog = FragmentUtils.showDialogRemoveOldOne(getFragmentManager(), this.dialog, newDialog, DIALOG_FRAGMENT_TAG);
        }
    }

    public void showErrorMsgAndExit(final CharSequence msg, final String optionalDetailLog) {
        LogUtils.e(TAG, "showErrorMsgAndExit: " + msg.toString());

        if (!canShowNewDialog())
            return;

        if (!isReadyToCommitFragments()) {
            addPendingTask(new Runnable() { public void run() { showErrorMsgAndExit(msg, optionalDetailLog); } });
        }
        else {
            wasShownDialogToFinish = true;
            DialogFragment newDialog = ErrorMsgDialog.newInstance(msg, true/*optionalDetailLog*/);
            this.dialog = FragmentUtils.showDialogRemoveOldOne(getFragmentManager(), this.dialog, newDialog, DIALOG_FRAGMENT_TAG);
        }
    }

    public void showErrorMsg(final CharSequence msg) {
        showPromptDialog(getResourcesSafe().getString(R.string.error), msg);
    }

    public void showWarningMsg(final CharSequence msg) {
        showPromptDialog(getResourcesSafe().getString(R.string.warning), msg);
    }

    public void showMsgNoTitle(final CharSequence msg) {
        showPromptDialog("", msg);
    }

    public void showPromptDialog(final CharSequence title, final CharSequence msg) {
        LogUtils.e(TAG, "showPromptDialog: " + title + ": " + msg.toString());

        if (!canShowNewDialog())
            return;

        if (!isReadyToCommitFragments()) {
            addPendingTask(new Runnable() { public void run() { showPromptDialog(title, msg); } });
        }
        else {
            this.dialog = PromptDialog.show(getFragmentManager(), this.dialog, DIALOG_FRAGMENT_TAG, null, title, msg, false);
        }
    }

// Radsi zaremuju... kazdopadne chci, aby si volajici explicitne vybral, jestli ma nebo nema byt cancelable
//    public void showProgressDialog(final CharSequence msg) {
//        showProgressDialog(msg, true);
//    }

    public void showProgressDialog(final CharSequence msg, final boolean cancelable) {
        if (!canShowNewDialog())
            return;

        if (!isReadyToCommitFragments()) {
            addPendingTask(new Runnable() { public void run() { showProgressDialog(msg, cancelable); } });
        }
        else {
            this.dialog = ProgressDialog.show(getFragmentManager(), this.dialog, DIALOG_FRAGMENT_TAG, null, msg, cancelable, false, null);
        }
    }

    public void hideProgressDialog() {
        if (!isReadyToCommitFragments()) {
            addPendingTask(new Runnable() { public void run() { hideProgressDialog(); } });
        }
        else {
            FragmentManager fm = getFragmentManager();
            if (this.dialog == null)
                this.dialog = (DialogFragment)fm.findFragmentByTag(DIALOG_FRAGMENT_TAG);
            if (this.dialog instanceof ProgressDialog) {
                fm.beginTransaction().remove(this.dialog).commit();
                this.dialog = null;
            }
        }
    }


    //
    // PRIVATE
    //

    private boolean canShowNewDialog() {
        if (wasShownDialogToFinish) {
            LogUtils.e(TAG, "Can't show new dialog after showing dialog to finish activity!");
            return false;
        }
        else
            return true;
    }


    //
    // INNER CLASSES
    //

    public interface RecognizedTextSetter {
        void onRecognizedText(String text);
    }

    public interface ITaskErrorWtDialog extends ITaskError {
        DialogFragment createDialog(ITaskContext context, boolean finish);
    }

    public static class ErrorMsgDialog extends BaseDialogFragment {
        private boolean finish;

        public static ErrorMsgDialog newInstance(CharSequence msg, boolean finish) {
            Bundle b = new Bundle();
            b.putCharSequence("msg", msg);
            b.putBoolean("finish", finish);
            ErrorMsgDialog ret = new ErrorMsgDialog();
            ret.setArguments(b);
            ret.setCancelable(true);
            return ret;
        }

        @Override
        protected Builder build(Builder b, Bundle savedInstanceState) {
            Bundle args = getArguments();
            final CharSequence errorMsg = args.getCharSequence("msg");
            this.finish = args.getBoolean("finish");

            b.setTitle(getString(R.string.error));
            b.setMessage(errorMsg);
            b.setPositiveButton(android.R.string.ok, new OnClickListener() {
                @Override
                public void onClick(View v) {
                    dismiss();
                    if (finish && getActivity() != null)
                        getActivity().finish();
                }
            });
            return b;
        }

        @Override
        public void onCancel(DialogInterface dialog) {
            super.onCancel(dialog);
            if (finish && getActivity() != null)
                getActivity().finish();
        }
    }
} public interface IDialogsFragmentActivity {
    DialogsFragment getDialogsFragment();
} public interface IBaseFragmentActivity {
    void addOnBackPressedListener(OnBackPressedListener l);
    void removeOnBackPressedListener(OnBackPressedListener l);
} public interface OnBackPressedListener {
    boolean onBackPressed();
} public interface ITaskError extends IApiParcelable {
    boolean isOk();
    CharSequence getMsg(ITaskContext context);
    //DialogFragment createDialog(ITaskContext context, boolean finish);
    void showToast(ITaskContext context);
    TaskException createExc(ITaskContext context);
    String getGoogleAnalyticsId();
} public class TaskFragment extends BaseRetainFragment {
    private static final String FRAGMENT_TAG = "TaskFragment";

    private ITaskExecutor executor;
    private final ArrayList<PendingTask> pendingPausedNewTasks = new ArrayList<PendingTask>();
    private final ArrayList<PendingTask> pendingCompletedTasks = new ArrayList<PendingTask>();
    private final ArrayList<ProgressTask> pendingProgressTasks = new ArrayList<ProgressTask>();
    private boolean readyToCompleteTasks = false;
    private boolean pauseNewTasks = false;


    public static <T extends FragmentActivity & ITaskFragmentActivity> TaskFragment getInstance(T activity) {
        FragmentManager fm = activity.getSupportFragmentManager();
        TaskFragment f = (TaskFragment)fm.findFragmentByTag(FRAGMENT_TAG);
        if (f == null) {
            f = new TaskFragment();
            setupNewFragment(f, activity);
            fm.beginTransaction().add(f, FRAGMENT_TAG).commit();
        }
        return f;
    }

    protected static void setupNewFragment(TaskFragment f, Activity activity) {
        f.executor = GlobalContextLib.get().getTaskExecutor();
    }


    //
    // Lifecycle methods
    //

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.executor = GlobalContextLib.get().getTaskExecutor();
    }

    @Override
    public void onResume() {
        super.onResume();
        this.readyToCompleteTasks = true;

        // musim udelat tak, aby tesne pred spustenim onTaskProgress byl task z pendingProgressTasks odstranen
        while (!this.pendingProgressTasks.isEmpty()) {
            ProgressTask t = this.pendingProgressTasks.get(0);
            this.pendingProgressTasks.remove(0);
            onTaskProgress(t.getId(), t.getParam(), t.getBundle(), t.getProgress(), t.getProgressState(), t.getFragmentTag());
        }

        finishPendingCompletedTasksIfCan();
    }

    @Override
    public void onPause() {
        this.readyToCompleteTasks = false;
        super.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        this.readyToCompleteTasks = false;
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        this.executor.cancelTasksByResultHandler(new TaskResultListener(this, true, null));
    }


    //
    // Public methods
    //

    public boolean isPauseNewTasks() {
        return this.pauseNewTasks;
    }

    public void setPauseNewTasks(boolean pauseNewTasks) {
        if (this.pauseNewTasks != pauseNewTasks) {
            this.pauseNewTasks = pauseNewTasks;

            if (!pauseNewTasks) {
                PendingTask[] tmp = this.pendingPausedNewTasks.toArray(new PendingTask[this.pendingPausedNewTasks.size()]);
                this.pendingPausedNewTasks.clear();

                for (PendingTask p : tmp) {
                    executeTask(p.getId(), p.getParam(), p.getBundle(), p.canCacheReferenceToParamResult(), p.getFragmentTag());
                }
            }
        }
    }

    public void executeTask(String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag) {
        if (pauseNewTasks) {
            pendingPausedNewTasks.add(new PendingTask(this, id, param, bundle, canCacheReferenceToParamResult, fragmentTag, null));
        }
        else {
            TaskResultListener t = new TaskResultListener(this, false, fragmentTag);
            this.executor.executeTask(id, param, bundle, canCacheReferenceToParamResult, t, t);
        }
    }

    public boolean containsTask(String id, String fragmentTag) {
        return getTask(id, fragmentTag) != null;
    }

    public boolean containsAnyTaskByFragmentTag(String fragmentTag) {
        if (getPendingTaskIndByFragmentTag(pendingPausedNewTasks, fragmentTag) >= 0
                || this.executor.containsAnyTaskByResultHandler(new TaskResultListener(this, false, fragmentTag))
                || getPendingTaskIndByFragmentTag(pendingCompletedTasks, fragmentTag) >= 0)
            return true;
        else
            return false;
    }

    public ITask getTask(String id, String fragmentTag) {
        int pausedNewTaskInd = getPendingTaskInd(pendingPausedNewTasks, id, fragmentTag);
        if (pausedNewTaskInd >= 0) {
            return pendingPausedNewTasks.get(pausedNewTaskInd);
        }

        ITask ret = this.executor.getTask(id, new TaskResultListener(this, false, fragmentTag));
        if (ret != null) {
            return ret;
        }

        int completedTaskInd = getPendingTaskInd(pendingCompletedTasks, id, fragmentTag);
        if (completedTaskInd >= 0) {
            return this.pendingCompletedTasks.get(completedTaskInd);
        }
        else
            return  null;
    }

    public boolean cancelTask(String id, String fragmentTag) {
        int pausedNewTaskInd = getPendingTaskInd(pendingPausedNewTasks, id, fragmentTag);
        if (pausedNewTaskInd >= 0) {
            this.pendingPausedNewTasks.remove(pausedNewTaskInd);
            return true;
        }

        // Chci mit opravdu jistotu, ze ruseny task uz nezavola ani onTaskProgress
        for (int i = 0; i < this.pendingProgressTasks.size(); i++) {
            ProgressTask p = this.pendingProgressTasks.get(i);

            if (EqualsUtils.equalsCheckNull(p.getId(), id) && EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) {
                pendingProgressTasks.remove(i);
                i--;
            }
        }

        boolean ret = this.executor.cancelTask(id, new TaskResultListener(this, false, fragmentTag));
        if (ret)
            return ret;

        int completedTaskInd = getPendingTaskInd(pendingCompletedTasks, id, fragmentTag);
        if (completedTaskInd >= 0) {
            this.pendingCompletedTasks.remove(completedTaskInd);
            return true;
        }
        else
            return false;
    }

    public void cancelTasksByFragmentId(String fragmentTag) {
        this.executor.cancelTasksByResultHandler(new TaskResultListener(this, false, fragmentTag));
        removePendingTasksByFragmentId(pendingPausedNewTasks, fragmentTag);
        removePendingTasksByFragmentId(pendingProgressTasks, fragmentTag);
        removePendingTasksByFragmentId(pendingCompletedTasks, fragmentTag);
    }

    public boolean addSkipCount(String id, String fragmentTag, int skipCount) {
        return this.executor.addSkipCount(id, new TaskResultListener(this, false, fragmentTag), skipCount);
    }

    // POZOR Pri pripadnem pridavani dalsich public metod myslet na WearableTaskFragment!!


    //
    // Callbacks
    //

    protected void onPreTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) {
        if (!this.readyToCompleteTasks)
            pendingProgressTasks.add(new ProgressTask(id, param, bundle, progress, progressState, fragmentTag));
        else
            onTaskProgress(id, param, bundle, progress, progressState, fragmentTag);
    }

    protected void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) {
        final Object listener;
        if (fragmentTag == null) {
            listener = getActivity();
        }
        else {
            listener = findTargetFragment(fragmentTag);
        }

        if (listener instanceof ITaskProgressListener) {
            ((ITaskProgressListener)listener).onTaskProgress(id, param, bundle, progress, progressState);
        }
    }


    protected void onPreTaskCompleted(String id, ITaskResult result, Bundle bundle, String fragmentTag) {
        if (!this.readyToCompleteTasks) {
            addPendingCompletedTask(id, result, bundle, false, fragmentTag);
        }
        else {
            onTaskCompleted(id, result, bundle, fragmentTag);
        }
    }

    protected void onTaskCompleted(String id, ITaskResult result, Bundle bundle, String fragmentTag) {
        final ITaskResultListener listener;
        if (fragmentTag == null) {
            listener = (ITaskResultListener)getActivity();
        }
        else {
            listener = findTargetFragment(fragmentTag);
        }

        if (listener != null) {
            listener.onTaskCompleted(id, result, bundle);
        }
    }

    protected void addPendingCompletedTask(String id, ITaskResult result, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag) {
        pendingCompletedTasks.add(new PendingTask(this, id, result.getParam(), bundle, canCacheReferenceToParamResult, fragmentTag, result));
    }

    protected void finishPendingCompletedTasksIfCan() {
        if (readyToCompleteTasks) {
            // musim udelat tak, aby tesne pred spustenim onTaskCompleted byl task z pendindCompletedTasks odstranen
            while (!this.pendingCompletedTasks.isEmpty()) {
                PendingTask t = this.pendingCompletedTasks.get(0);
                this.pendingCompletedTasks.remove(0);
                onTaskCompleted(t.getId(), t.getResult(), t.getBundle(), t.getFragmentTag());
            }
        }
    }


    //
    // PRIVATE
    //

    private ITaskResultListener findTargetFragment(String fragmentTag) {
        return (ITaskResultListener)FragmentUtils.findFragmentByNestedTag(getActivity(), fragmentTag);
    }

    protected static int getPendingTaskInd(ArrayList<PendingTask> tasks, String id, String fragmentTag) {
        for (int i = 0; i < tasks.size(); i++) {
            PendingTask p = tasks.get(i);
            if (EqualsUtils.equalsCheckNull(p.getId(), id) && EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) {
                return i;
            }
        }
        return -1;
    }

    protected static int getPendingTaskIndByFragmentTag(ArrayList<PendingTask> tasks, String fragmentTag) {
        for (int i = 0; i < tasks.size(); i++) {
            PendingTask p = tasks.get(i);
            if (EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) {
                return i;
            }
        }
        return -1;
    }

    protected static void removePendingTasksByFragmentId(ArrayList<? extends IHasFragmentTag> tasks, String fragmentTag) {
        for (int i = 0; i < tasks.size(); i++) {
            IHasFragmentTag p = tasks.get(i);
            if (EqualsUtils.equalsCheckNull(p.getFragmentTag(), fragmentTag)) {
                tasks.remove(i);
                i--;
            }
        }
    }


    //
    // INNER CLASSES
    //

    private static class TaskResultListener implements ITaskResultListener, ITaskProgressListener {
        private final TaskFragment taskFragment;
        private final boolean ignoreFragmentTagForEquals;
        private final String fragmentTag;

        private TaskResultListener(TaskFragment taskFragment, boolean ignoreFragmentTagForEquals, String fragmentTag) {
            this.taskFragment = taskFragment;
            this.ignoreFragmentTagForEquals = ignoreFragmentTagForEquals;
            this.fragmentTag = fragmentTag;
        }

        @Override
        public void onTaskCompleted(String id, ITaskResult result, Bundle bundle) {
            this.taskFragment.onPreTaskCompleted(id, result, bundle, this.fragmentTag);
        }

        @Override
        public void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState) {
            this.taskFragment.onPreTaskProgress(id, param, bundle, progress, progressState, fragmentTag);
        }


        @Override
        public int hashCode() {
            // Nemuzu brat v potaz fragmentTag kvuli ignoreFragmentTagForEquals
            return taskFragment.hashCode();
        }

        /**
         * Pokud je ignoreFragmentTagForEquals jednoho z obou objektu = true, tak se nebere v potaz fragmentTag
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (!(o instanceof TaskResultListener)) {
                return false;
            }

            TaskResultListener lhs = (TaskResultListener) o;
            return lhs != null &&
                    taskFragment.equals(lhs.taskFragment) &&
                    (ignoreFragmentTagForEquals || lhs.ignoreFragmentTagForEquals || (EqualsUtils.equalsCheckNull(fragmentTag, lhs.fragmentTag)));
        }
    }


    protected interface IHasFragmentTag {
        String getFragmentTag();
    }

    protected static class PendingTask implements IHasFragmentTag, ITask, ITaskResultListener, ITaskProgressListener {
        private final TaskFragment taskFragment;
        private final String id;
        private final ITaskParam param;
        private final Bundle bundle;
        private final boolean canCacheReferenceToParamResult;
        private final String fragmentTag;
        private final ITaskResult result; // optional - jenom u uz hotovych tasku...
        private final long timeStamp;

        private HashMap<String, Object> processObjects; // lazy loaded

        // Upraveno!
        public PendingTask(TaskFragment taskFragment, String id, ITaskParam param, Bundle bundle, boolean canCacheReferenceToParamResult, String fragmentTag, ITaskResult result) {
            this.taskFragment = taskFragment;
            this.id = id;
            this.param = param;
            this.bundle = bundle;
            this.canCacheReferenceToParamResult = canCacheReferenceToParamResult;
            this.fragmentTag = fragmentTag;
            this.result = result;

            this.timeStamp = SystemClock.elapsedRealtime();
        }

        public String getId() {
            return this.id;
        }

        public ITaskParam getParam() {
            return this.param;
        }

        public Bundle getBundle() {
            return this.bundle;
        }

        @Override
        public Object putProcessObj(String key, Object obj) {
            if (processObjects == null)
                processObjects = new HashMap<>();
            return processObjects.put(key, obj);
        }

        @Override
        public <T> T getProcessObj(String key) {
            if (processObjects == null)
                return null;
            else
                return (T)processObjects.get(key);
        }

        public String getFragmentTag() {
            return this.fragmentTag;
        }

        public ITaskResult getResult() {
            return this.result;
        }

        public long getTimeStamp() {
            return this.timeStamp;
        }

        @Override
        public boolean isCanceled() {
            return false;
        }

        @Override
        public int getSkipCount() {
            return 0;
        }

        @Override
        public int getProgress() {
            return 0;
        }

        @Override
        public String getProgressState() {
            return null;
        }

        @Override
        public ITaskResultListener getListener() {
            return this;
        }

        @Override
        public boolean canCacheReferenceToParamResult() {
            return canCacheReferenceToParamResult;
        }

        @Override
        public void onTaskProgress(int progress, String progressState) {
            onTaskProgress(id, param, bundle, progress, progressState);
        }

        @Override
        public void onTaskCompleted(String id, ITaskResult result, Bundle bundle) {
            this.taskFragment.onPreTaskCompleted(id, result, bundle, this.fragmentTag);
        }

        @Override
        public void onTaskProgress(String id, ITaskParam param, Bundle bundle, int progress, String progressState) {
            this.taskFragment.onPreTaskProgress(id, param, bundle, progress, progressState, fragmentTag);
        }
    }

    private static class ProgressTask implements IHasFragmentTag {
        private final String id;
        private final ITaskParam param;
        private final Bundle bundle;
        private final int progress;
        private final String progressState;
        private final String fragmentTag;

        public ProgressTask(String id, ITaskParam param, Bundle bundle, int progress, String progressState, String fragmentTag) {
            this.id = id;
            this.param = param;
            this.bundle = bundle;
            this.progress = progress;
            this.progressState = progressState;
            this.fragmentTag = fragmentTag;
        }

        public String getId() {
            return this.id;
        }

        public ITaskParam getParam() {
            return this.param;
        }

        public Bundle getBundle() {
            return this.bundle;
        }

        public int getProgress() {
            return this.progress;
        }

        public String getProgressState() {
            return this.progressState;
        }

        public String getFragmentTag() {
            return this.fragmentTag;
        }
    }


    public interface ITaskFragmentActivity {
        TaskFragment getTaskFragment();
    }
} public interface ITaskFragmentActivity {
    TaskFragment getTaskFragment();
} public interface ITaskResult {
    ITaskParam getParam();
    boolean isValidResult();
    boolean isCacheableResult();
    boolean canUseCachedResultNow();
    ITaskError getError();
} public interface ITaskResultListener {
    void onTaskCompleted(String id, ITaskResult result, Bundle bundle);
} public class ActivityUtils {
    private static final String ACTIVITY_RESULT_PARCELABLE = ActivityUtils.class.getName() + "." + "ACTIVITY_RESULT_PARCELABLE";

    public static void setResultParcelable(Activity activity, int resultCode, Parcelable data) {
        Intent intent = new Intent();
        intent.putExtra(ACTIVITY_RESULT_PARCELABLE, data);
        activity.setResult(resultCode, intent);
    }

    public static <T extends Parcelable> T getResultParcelable(Intent resultData) {
        return resultData != null ? resultData.<T>getParcelableExtra(ACTIVITY_RESULT_PARCELABLE) : null;
    }

} public class EqualsUtils {
    public static int HASHCODE_INVALID = 654123456;
    public static int HASHCODE_INVALID_REPLACEMENT = 654123457;

    public static <T> int hashCodeCheckNull(T o) {
        return o == null ? 0 : o.hashCode();
    }

    public static <T> boolean equalsCheckNull(T lhs, T rhs) {
        return lhs == null ? rhs == null : lhs.equals(rhs);
    }

    public static int itemsHashCode(List<?> items) {
        int result = 17;
        if (items != null) {
            for (int i = 0; i < items.size(); i++) {
                result = 31 * result + (items.get(i) == null ? 0 : items.get(i).hashCode());
            }
        }
        return result;
    }

    public static int itemsHashCode(int[] items) {
        int result = 17;
        if (items != null) {
            for (int i = 0; i < items.length; i++) {
                result = 31 * result + items[i];
            }
        }
        return result;
    }


    public static boolean itemsEqual(List<?> lhs, List<?> rhs) {
        if (lhs == rhs)
            return true;
        if (lhs == null || rhs == null)
            return false;
        if (lhs.size() != rhs.size())
            return false;

        for (int i = 0; i < lhs.size(); i++) {
            if (lhs.get(i) == null) {
                if (rhs.get(i) != null)
                    return false;
            }
            else if (!lhs.get(i).equals(rhs.get(i))) {
                return false;
            }
        }
        return true;
    }

    public static boolean itemsEqual(int[] lhs, int[] rhs) {
        if (lhs == rhs)
            return true;
        if (lhs == null || rhs == null || lhs.length != rhs.length)
            return false;

        for (int i = 0; i < lhs.length; i++) {
            if (lhs[i] != rhs[i])
                return false;
        }
        return true;
    }

    public static int itemsOfItemsHashCode(List<? extends List<?>> items) {
        int result = 17;
        if (items != null) {
            for (int i = 0; i < items.size(); i++) {
                result = 31 * result + itemsHashCode(items.get(i));
            }
        }
        return result;
    }

    public static boolean itemsOfItemsEqual(List<? extends List<?>> lhs, List<? extends List<?>> rhs) {
        if (lhs == rhs)
            return true;
        if (lhs == null || rhs == null)
            return false;
        if (lhs.size() != rhs.size())
            return false;

        for (int i = 0; i < lhs.size(); i++) {
            if (!itemsEqual(lhs.get(i), rhs.get(i))) {
                return false;
            }
        }
        return true;
    }
} public class EnterPassphraseActivity extends BaseActivity {

    // Views
    private TextInputLayout inputLayoutPass1;
    private EditText editTextPass1;
    private TextInputLayout inputLayoutPass2;
    private EditText editTextPass2;
    private View btnPassphraseVisibility;
    private Button btnCancel;
    private Button btnConfirm;

    // Immutable members
    private EnterPassphraseActivityParam activityParam;

    // Unsave state
    private boolean passphraseVisible = false;

    public static Intent createIntent(Context context, EnterPassphraseActivityParam activityParam) {
        return new Intent(context, EnterPassphraseActivity.class).putExtra("activityParam", activityParam);
    }


    //
    // LIFECYCLE CALLBACKS
    //

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

        this.inputLayoutPass1 = (TextInputLayout)findViewById(R.id.input_layout_pass1);
        this.editTextPass1 = (EditText)findViewById(R.id.edit_text_pass1);
        this.inputLayoutPass2 = (TextInputLayout)findViewById(R.id.input_layout_pass2);
        this.editTextPass2 = (EditText)findViewById(R.id.edit_text_pass2);
        this.btnPassphraseVisibility = (View)findViewById(R.id.btn_passphrase_visibility);
        this.btnCancel = (Button)findViewById(R.id.btn_cancel);
        this.btnConfirm = (Button)findViewById(R.id.btn_confirm);

        this.activityParam = getIntent().getParcelableExtra("activityParam");

        TextWatcher textWatcher = new TextWatcher() {
            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
            @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }

            @Override
            public void afterTextChanged(Editable s) {
                boolean valid = setupTextInputLayout(inputLayoutPass2, editTextPass2.getText().toString(),
                        TextUtils.equals(editTextPass1.getText(), editTextPass2.getText()), R.string.enter_passphrase_no_match);

                btnConfirm.setEnabled(valid && editTextPass1.getText().length() > 0);
            }
        };

        editTextPass1.addTextChangedListener(textWatcher);
        editTextPass2.addTextChangedListener(textWatcher);

        btnPassphraseVisibility.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                passphraseVisible = !passphraseVisible;
                if (passphraseVisible) {
                    editTextPass1.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                    editTextPass2.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
                }
                else {
                    editTextPass1.setTransformationMethod(PasswordTransformationMethod.getInstance());
                    editTextPass2.setTransformationMethod(PasswordTransformationMethod.getInstance());
                }
            }
        });
        btnCancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        btnConfirm.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                String passphrase = editTextPass1.getText().toString();

                if (passphrase.length() > 0 && passphrase.equals(editTextPass2.getText().toString())) {
                    ActivityUtils.setResultParcelable(EnterPassphraseActivity.this, RESULT_OK, new EnterPassphraseActivityResult(activityParam, passphrase));
                    finish();
                }
            }
        });

        ActivityUtils.setResultParcelable(EnterPassphraseActivity.this, RESULT_CANCELED, new EnterPassphraseActivityResult(activityParam, ""));

        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }


    //
    // PRIVATE
    //

    private static boolean setupTextInputLayout(TextInputLayout inputLayout, String enteredText, boolean isValid, int errorRid) {
        if (!isValid) {
            if (enteredText.isEmpty()) {
                inputLayout.setErrorEnabled(false);
                inputLayout.setError(null);
            }
            else {
                inputLayout.setErrorEnabled(true);
                inputLayout.setError(inputLayout.getContext().getString(errorRid));
            }
            return false;
        }
        else {
            inputLayout.setErrorEnabled(false);
            inputLayout.setError(null);
            return true;
        }
    }


    //
    // INNER CLASSES
    //

    public static class EnterPassphraseActivityParam extends ApiParcelable {
        public final String taskId;
        public final IApiParcelable tag; // optional abstract

        public EnterPassphraseActivityParam(String taskId, IApiParcelable tag) {
            this.taskId = taskId;
            this.tag = tag;
        }

        public EnterPassphraseActivityParam(ApiDataInput d) {
            this.taskId = d.readString();
            this.tag = d.readOptParcelableWithName();
        }

        @Override
        public void save(ApiDataOutput d, int flags) {
            d.write(this.taskId);
            d.writeOptWithName(this.tag, flags);
        }

        public static final ApiCreator<EnterPassphraseActivityParam> CREATOR = new ApiCreator<EnterPassphraseActivityParam>() {
            public EnterPassphraseActivityParam create(ApiDataInput d) { return new EnterPassphraseActivityParam(d); }
            public EnterPassphraseActivityParam[] newArray(int size) { return new EnterPassphraseActivityParam[size]; }
        };
    }

    public static class EnterPassphraseActivityResult extends ApiParcelable {
        public final EnterPassphraseActivityParam param;
        public final String passphrase;

        public EnterPassphraseActivityResult(EnterPassphraseActivityParam param, String passphrase) {
            this.param = param;
            this.passphrase = passphrase;
        }

        public EnterPassphraseActivityResult(ApiDataInput d) {
            this.param = d.readObject(EnterPassphraseActivityParam.CREATOR);
            this.passphrase = d.readString();
        }

        @Override
        public void save(ApiDataOutput d, int flags) {
            d.write(this.param, flags);
            d.write(this.passphrase);
        }

        public static final ApiCreator<EnterPassphraseActivityResult> CREATOR = new ApiCreator<EnterPassphraseActivityResult>() {
            public EnterPassphraseActivityResult create(ApiDataInput d) { return new EnterPassphraseActivityResult(d); }
            public EnterPassphraseActivityResult[] newArray(int size) { return new EnterPassphraseActivityResult[size]; }
        };
    }
} public static class EnterPassphraseActivityParam extends ApiParcelable {
    public final String taskId;
    public final IApiParcelable tag; // optional abstract

    public EnterPassphraseActivityParam(String taskId, IApiParcelable tag) {
        this.taskId = taskId;
        this.tag = tag;
    }

    public EnterPassphraseActivityParam(ApiDataInput d) {
        this.taskId = d.readString();
        this.tag = d.readOptParcelableWithName();
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.taskId);
        d.writeOptWithName(this.tag, flags);
    }

    public static final ApiCreator<EnterPassphraseActivityParam> CREATOR = new ApiCreator<EnterPassphraseActivityParam>() {
        public EnterPassphraseActivityParam create(ApiDataInput d) { return new EnterPassphraseActivityParam(d); }
        public EnterPassphraseActivityParam[] newArray(int size) { return new EnterPassphraseActivityParam[size]; }
    };
} public static class EnterPassphraseActivityResult extends ApiParcelable {
    public final EnterPassphraseActivityParam param;
    public final String passphrase;

    public EnterPassphraseActivityResult(EnterPassphraseActivityParam param, String passphrase) {
        this.param = param;
        this.passphrase = passphrase;
    }

    public EnterPassphraseActivityResult(ApiDataInput d) {
        this.param = d.readObject(EnterPassphraseActivityParam.CREATOR);
        this.passphrase = d.readString();
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.param, flags);
        d.write(this.passphrase);
    }

    public static final ApiCreator<EnterPassphraseActivityResult> CREATOR = new ApiCreator<EnterPassphraseActivityResult>() {
        public EnterPassphraseActivityResult create(ApiDataInput d) { return new EnterPassphraseActivityResult(d); }
        public EnterPassphraseActivityResult[] newArray(int size) { return new EnterPassphraseActivityResult[size]; }
    };
} public class EnterPinActivity extends BaseActivity {
    private static final int PIN_MAX_LENGTH = 9;

    // Views
    private TextView txtTitle;
    private TextView txtText;
    private TextView txtPinStars;
    private View btnBackspace;
    private TextView txtPinStrength;
    private View btnCancel;
    private View btnConfirm;
    private ImmutableList<View> buttons;

    // Immutable members
    private GlobalContext gct;
    private EnterPinActivityParam activityParam;
    private final int[] tmpNumCounts = new int[9];



    public static Intent createIntent(Context context, EnterPinActivityParam activityParam) {
        return new Intent(context, EnterPinActivity.class).putExtra("activityParam", activityParam);
    }


    //
    // LIFECYCLE CALLBACKS
    //


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

        //final CustomActionBar actionBar = (CustomActionBar)findViewById(R.id.action_bar);
        //actionBar.initWithoutUpButton(this);

        this.txtTitle = (TextView)findViewById(R.id.txt_title);
        this.txtText = (TextView)findViewById(R.id.txt_text);
        this.txtPinStars = (TextView)findViewById(R.id.txt_pin_stars);
        this.btnBackspace = (View)findViewById(R.id.btn_backspace);
        this.txtPinStrength = (TextView)findViewById(R.id.txt_pin_strength);
        this.btnCancel = (View)findViewById(R.id.btn_cancel);
        this.btnConfirm = (View)findViewById(R.id.btn_confirm);

        this.gct = GlobalContext.get();
        this.activityParam = getIntent().getParcelableExtra("activityParam");

        ImmutableList.Builder<View> bButtons = ImmutableList.builder();
        addPinButton(bButtons, R.id.button1, 1);
        addPinButton(bButtons, R.id.button2, 2);
        addPinButton(bButtons, R.id.button3, 3);
        addPinButton(bButtons, R.id.button4, 4);
        addPinButton(bButtons, R.id.button5, 5);
        addPinButton(bButtons, R.id.button6, 6);
        addPinButton(bButtons, R.id.button7, 7);
        addPinButton(bButtons, R.id.button8, 8);
        addPinButton(bButtons, R.id.button9, 9);
        this.buttons = bButtons.build();

        btnBackspace.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                String s = txtPinStars.getText().toString();
                if (s.length() > 0) {
                    txtPinStars.setText(s.substring(0, s.length() - 1));
                }
            }
        });

        txtPinStrength.setVisibility(activityParam.pinMatrixRequestType != PinMatrixRequestType.PinMatrixRequestType_Current ? View.VISIBLE : View.GONE);

        txtPinStars.addTextChangedListener(new TextWatcher() {

            @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
            @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { }

            @Override
            public void afterTextChanged(Editable editable) {
                if (editable.length() == 0) {
                    btnConfirm.setEnabled(false);
                    txtPinStrength.setText(String.valueOf(CustomHtml.HARD_SPACE));
                }
                else {
                    btnConfirm.setEnabled(true);

                    for (int i = 0; i < tmpNumCounts.length; i++)
                        tmpNumCounts[i] = 0;

                    for (int i = 0; i < editable.length(); i++) {
                        int ind = (int) editable.charAt(i) - 49;
                        tmpNumCounts[ind]++;
                    }

                    int diffDigits = 0;
                    for (int i = 0; i < tmpNumCounts.length; i++) {
                        if (tmpNumCounts[i] > 0)
                            diffDigits++;
                    }

                    if (diffDigits < 4) {
                        final int problemColor = ContextCompat.getColor(EnterPinActivity.this, R.color.text_problem);
                        final String html = CustomHtml.getFontColorTag(getString(R.string.enter_pin_strength_weak), problemColor);
                        txtPinStrength.setText(CustomHtml.fromHtmlWithCustomSpans(html));
                    } else if (diffDigits < 6) txtPinStrength.setText(R.string.enter_pin_strength_fine);
                    else if (diffDigits < 8) txtPinStrength.setText(R.string.enter_pin_strength_strong);
                    else txtPinStrength.setText(R.string.enter_pin_strength_ultimate);
                }

                boolean pinBtnsEnabled = editable.length() < PIN_MAX_LENGTH;
                for (View button : buttons)
                    button.setEnabled(pinBtnsEnabled);
            }
        });

        btnCancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        btnConfirm.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                String pin = txtPinStars.getText().toString();
                if (pin.length() > 0) {
                    ActivityUtils.setResultParcelable(EnterPinActivity.this, RESULT_OK, new EnterPinActivityResult(activityParam, pin));
                    finish();
                }
            }
        });

        switch (activityParam.pinMatrixRequestType) {
            case PinMatrixRequestType_Current:
                txtTitle.setText(R.string.enter_pin_current_prompt);
                txtText.setText(R.string.enter_pin_text);
                break;

            case PinMatrixRequestType_NewFirst:
                txtTitle.setText(R.string.enter_pin_new_prompt);
                txtText.setText(R.string.enter_pin_text);
                break;

            case PinMatrixRequestType_NewSecond:
                txtTitle.setText(R.string.enter_pin_repeat_prompt);
                txtText.setText(R.string.enter_pin_text_repeat);
                break;

            default: throw new NotImplementedException();
        }

        if (savedInstanceState == null) {
            txtPinStars.setText("");
        }

        ActivityUtils.setResultParcelable(this, RESULT_CANCELED, new EnterPinActivityResult(activityParam, ""));
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("tmp", true);
    }


    //
    // PRIVATE
    //

    void addPinButton(ImmutableList.Builder<View> bButtons, int rid, final int number) {
        View button = findViewById(rid);
        button.setOnClickListener(new OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View view) {
                if (txtPinStars.getText().length() < PIN_MAX_LENGTH) {
                    txtPinStars.setText(txtPinStars.getText().toString() + number);
                }
            }
        });
        bButtons.add(button);
    }


    //
    // INNER CLASSES
    //

    public static class EnterPinActivityParam extends ApiParcelable {
        public final String taskId;
        public final IApiParcelable tag; // optional abstract
        public final PinMatrixRequestType pinMatrixRequestType;

        public EnterPinActivityParam(String taskId, IApiParcelable tag, PinMatrixRequestType pinMatrixRequestType) {
            this.taskId = taskId;
            this.tag = tag;
            this.pinMatrixRequestType = pinMatrixRequestType;
        }

        public EnterPinActivityParam(ApiDataInput d) {
            this.taskId = d.readString();
            this.tag = d.readOptParcelableWithName();
            this.pinMatrixRequestType = PinMatrixRequestType.valueOf(d.readString());
        }

        @Override
        public void save(ApiDataOutput d, int flags) {
            d.write(this.taskId);
            d.writeOptWithName(this.tag, flags);
            d.write(this.pinMatrixRequestType.name());
        }

        public static final ApiCreator<EnterPinActivityParam> CREATOR = new ApiCreator<EnterPinActivityParam>() {
            public EnterPinActivityParam create(ApiDataInput d) { return new EnterPinActivityParam(d); }
            public EnterPinActivityParam[] newArray(int size) { return new EnterPinActivityParam[size]; }
        };
    }

    public static class EnterPinActivityResult extends ApiParcelable {
        public final EnterPinActivityParam param;
        public final String pinEncoded;

        public EnterPinActivityResult(EnterPinActivityParam param, String pinEncoded) {
            this.param = param;
            this.pinEncoded = pinEncoded;
        }

        public EnterPinActivityResult(ApiDataInput d) {
            this.param = d.readObject(EnterPinActivityParam.CREATOR);
            this.pinEncoded = d.readString();
        }

        @Override
        public void save(ApiDataOutput d, int flags) {
            d.write(this.param, flags);
            d.write(this.pinEncoded);
        }

        public static final ApiCreator<EnterPinActivityResult> CREATOR = new ApiCreator<EnterPinActivityResult>() {
            public EnterPinActivityResult create(ApiDataInput d) { return new EnterPinActivityResult(d); }
            public EnterPinActivityResult[] newArray(int size) { return new EnterPinActivityResult[size]; }
        };
    }
} public static class EnterPinActivityParam extends ApiParcelable {
    public final String taskId;
    public final IApiParcelable tag; // optional abstract
    public final PinMatrixRequestType pinMatrixRequestType;

    public EnterPinActivityParam(String taskId, IApiParcelable tag, PinMatrixRequestType pinMatrixRequestType) {
        this.taskId = taskId;
        this.tag = tag;
        this.pinMatrixRequestType = pinMatrixRequestType;
    }

    public EnterPinActivityParam(ApiDataInput d) {
        this.taskId = d.readString();
        this.tag = d.readOptParcelableWithName();
        this.pinMatrixRequestType = PinMatrixRequestType.valueOf(d.readString());
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.taskId);
        d.writeOptWithName(this.tag, flags);
        d.write(this.pinMatrixRequestType.name());
    }

    public static final ApiCreator<EnterPinActivityParam> CREATOR = new ApiCreator<EnterPinActivityParam>() {
        public EnterPinActivityParam create(ApiDataInput d) { return new EnterPinActivityParam(d); }
        public EnterPinActivityParam[] newArray(int size) { return new EnterPinActivityParam[size]; }
    };
} public static class EnterPinActivityResult extends ApiParcelable {
    public final EnterPinActivityParam param;
    public final String pinEncoded;

    public EnterPinActivityResult(EnterPinActivityParam param, String pinEncoded) {
        this.param = param;
        this.pinEncoded = pinEncoded;
    }

    public EnterPinActivityResult(ApiDataInput d) {
        this.param = d.readObject(EnterPinActivityParam.CREATOR);
        this.pinEncoded = d.readString();
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.param, flags);
        d.write(this.pinEncoded);
    }

    public static final ApiCreator<EnterPinActivityResult> CREATOR = new ApiCreator<EnterPinActivityResult>() {
        public EnterPinActivityResult create(ApiDataInput d) { return new EnterPinActivityResult(d); }
        public EnterPinActivityResult[] newArray(int size) { return new EnterPinActivityResult[size]; }
    };
} public class MainActivity extends BaseActivity implements OnPromptDialogDone, OnEnterTextDialogDone, OnCheckConfirmedDialogDone {
    private static final String TAG = MainActivity.class.getSimpleName();

    private static final String TASK_TRY_CONNECT_TREZOR = "TASK_TRY_CONNECT_TREZOR";
    private static final String TASK_CHANGE_SETTING_PREFIX = "TASK_CHANGE_SETTING:";

    private static final String POSTFIX_COMMON = "COMMON";
    private static final String POSTFIX_DISCONNECT_TO_APPLY_SETTING = "DISCONNECT_TO_APPLY_SETTING";
    private static final String POSTFIX_DISCONNECT_WIPED = "DISCONNECT_WIPED";

    private static final String DIALOG_CONFIRM_ACTION = "DIALOG_CONFIRM_ACTION";
    private static final String DIALOG_CHANGE_LABEL = "DIALOG_CHANGE_LABEL";
    private static final String DIALOG_DISABLE_PIN = "DIALOG_DISABLE_PIN";
    private static final String DIALOG_DISABLE_PASSPHRASE= "DIALOG_DISABLE_PASSPHRASE";
    private static final String DIALOG_WIPE = "DIALOG_WIPE";
    private static final String DIALOG_DISCONNECT_TREZOR = "DIALOG_DISCONNECT_TREZOR";
    private static final String DIALOG_FORGET_DEVICE = "DIALOG_FORGET_DEVICE";


    private NavDrawer navDrawer;
    private ProgressBar progressBar;
    private NestedScrollView nestedScrollView;
    private View rootDisconnected;
    private View rootConnected;

    private Button btnLaunchWebwallet;
    private Button btnChangeLabel;
    private Button btnChangeHomescreen;
    private TextView txtSecuritySetup;
    private ViewHolderSwitch switchPin;
    private Button btnChangePin;
    private ViewHolderSwitch switchPassphrase;
    private TextView txtDeviceSetup;
    private Button btnFirmwareVersion;
    //private Button btnForgetDevice;
    private Button btnWipeDevice;
    private Button btnNotification;

    private GlobalContext gct;

    // Saved state
    private InitializedTrezor initializedTrezor; // trezor nemusi byt aktualne pripojen
    private String connectedTrezorDeviceId = "";
    private long unlockedTimestamp = Long.MIN_VALUE; // cas v System.elapsedTime, kdy byl trezor odemknut
    private PromptDialog dialogConfirmAction;
    private PromptDialog dialogDisconnectTrezor;


    public static Intent createIntent(Context context) {
        Intent ret = new Intent(context, MainActivity.class);
        ret.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        return ret;
    }

    //
    // LIFECYCLE CALLBACKS
    //

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Couple<ViewGroup, CustomActionBar> rootWithActionBar = CustomActionBar.inflateWithActionBar(this, R.layout.main_activity);
        this.navDrawer = NavDrawer.setContentView(this, rootWithActionBar.getFirst());
        if (!getResources().getBoolean(R.bool.nav_drawer_permanent))
            rootWithActionBar.getSecond().setNavigationButtonNavDrawer(navDrawer);

        this.progressBar = (ProgressBar)findViewById(R.id.progress_bar);
        this.nestedScrollView = (NestedScrollView)findViewById(R.id.nested_scroll_view);
        this.rootDisconnected = findViewById(R.id.root_disconnected);
        this.rootConnected = findViewById(R.id.root_connected);

        this.btnLaunchWebwallet = (Button)findViewById(R.id.btn_launch_webwallet);
        this.btnChangeLabel = (Button)findViewById(R.id.btn_change_label);
        this.btnChangeHomescreen = (Button)findViewById(R.id.btn_change_homescreen);
        this.txtSecuritySetup = (TextView) findViewById(R.id.txt_security_setup);
        this.switchPin = new ViewHolderSwitch(findViewById(R.id.switch_pin));
        this.btnChangePin = (Button)findViewById(R.id.btn_change_pin);
        this.switchPassphrase = new ViewHolderSwitch(findViewById(R.id.switch_passphrase));
        this.txtDeviceSetup = (TextView) findViewById(R.id.txt_device_setup);
        this.btnFirmwareVersion = (Button)findViewById(R.id.btn_firmware_version);
        //this.btnForgetDevice = (Button)findViewById(R.id.btn_forget_device);
        this.btnWipeDevice = (Button)findViewById(R.id.btn_wipe_device);
        this.btnNotification = (Button)findViewById(R.id.btn_notification);

        this.gct = GlobalContext.get();

        this.dialogConfirmAction = (PromptDialog)getSupportFragmentManager().findFragmentByTag(DIALOG_CONFIRM_ACTION);
        this.dialogDisconnectTrezor = (PromptDialog)getSupportFragmentManager().findFragmentByTag(DIALOG_DISCONNECT_TREZOR);

        btnLaunchWebwallet.setText(R.string.main_activity_launch_webwallet);
        btnChangeLabel.setText(R.string.main_activity_change_label);
        btnChangeHomescreen.setText(R.string.main_activity_change_homescreen);
        txtSecuritySetup.setText(R.string.main_activity_security_setup);
        switchPin.text.setText(R.string.main_activity_pin);
        btnChangePin.setText(R.string.main_activity_change_pin);
        setupTwoLinesText(switchPassphrase.text, R.string.main_activity_passphrase_title, R.string.main_activity_passphrase_text);
        txtDeviceSetup.setText(R.string.main_activity_device_setup);
        btnFirmwareVersion.setText(R.string.main_activity_firmware_version);
        //setupTwoLinesText(btnForgetDevice, R.string.main_activity_forget_device_title, R.string.main_activity_forget_device_text);
        setupTwoLinesText(btnWipeDevice, R.string.main_activity_wipe_device_title, R.string.main_activity_wipe_device_text);

        btnLaunchWebwallet.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    String urlString = "https://beta-wallet.trezor.io";
                    String browserPackage = "com.android.chrome";
                    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlString));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.setPackage(browserPackage);
                    try {
                        startActivity(intent);
                    } catch (android.content.ActivityNotFoundException ex) {
                        // Chrome not installed
                        try {
                            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + browserPackage)));
                        } catch (android.content.ActivityNotFoundException anfe) {
                            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + browserPackage)));
                        }
                    }
                }
                catch (Exception ex) {
                    LogUtils.e(TAG, "btnLaunchWebwallet onClick thrown exception", ex);
                    BaseError.ERR_UNKNOWN_ERROR.showToast(gct);
                }
//                if (initializedTrezor != null) {
//                    startActivity(XpubActivity.createIntent(view.getContext(), initializedTrezor.getPublicKey().getXpub()));
//                }
            }
        });
        btnChangeLabel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    EnterTextDialog.show(getSupportFragmentManager(), null, DIALOG_CHANGE_LABEL, DIALOG_CHANGE_LABEL,
                            getString(R.string.main_activity_change_label),
                            getString(R.string.main_activity_change_label_prompt),
                            getString(R.string.init_trezor_device_label_hint),
                            "");
                }
            }
        });
        btnChangeHomescreen.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    startActivityForResult(ChangeHomescreenActivity.createIntent(view.getContext(), initializedTrezor.getFeatures().getLabel(), TextUtils.equals(initializedTrezor.getFeatures().getModel(), "T")), GlobalContext.RQC_PICK_HOMESCREEN);
                    setDontDisconnectOnStop();
                }
            }
        });
        switchPin.button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    if (initializedTrezor.getFeatures().getPinProtection()) {
                        CheckConfirmedDialog.show(getSupportFragmentManager(), null, DIALOG_DISABLE_PIN, DIALOG_DISABLE_PIN,
                                getString(R.string.warning),
                                getString(R.string.main_activity_disable_pin_prompt),
                                getString(R.string.i_understand),
                                getString(R.string.main_activity_disable_pin));
                    }
                    else {
                        executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ChangePin.newBuilder().build());
                        refreshGui();
                    }
                }
            }
        });
        btnChangePin.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ChangePin.newBuilder().build());
                }
            }
        });
        switchPassphrase.button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    if (initializedTrezor.getFeatures().getPassphraseProtection()) {
                        CheckConfirmedDialog.show(getSupportFragmentManager(), null, DIALOG_DISABLE_PASSPHRASE, DIALOG_DISABLE_PASSPHRASE,
                                getString(R.string.warning),
                                getString(R.string.main_activity_disable_passphrase_prompt),
                                getString(R.string.i_understand),
                                getString(R.string.main_activity_disable_passphrase));
                    }
                    else {
                        executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ApplySettings.newBuilder().setUsePassphrase(true).build());
                        refreshGui();
                    }
                }
            }
        });
//        btnForgetDevice.setOnClickListener(new OnClickListener() {
//            @Override
//            public void onClick(View view) {
//                PromptDialog.show(getSupportFragmentManager(), null, DIALOG_FORGET_DEVICE, DIALOG_FORGET_DEVICE, getString(R.string.main_activity_forget_device_title), getString(R.string.main_activity_forget_device_prompt),
//                        true, true, true, null, getString(R.string.main_activity_forget_device_title), getString(R.string.cancel));
//            }
//        });
        btnWipeDevice.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if (initializedTrezor != null && initializedTrezor.getFeatures().getDeviceId().equals(connectedTrezorDeviceId)) {
                    CheckConfirmedDialog.show(getSupportFragmentManager(), null, DIALOG_WIPE, DIALOG_WIPE,
                            getString(R.string.warning),
                            CustomHtml.fromHtmlWithCustomSpans(CustomHtml.getFontColorTag(getString(R.string.main_activity_wipe_device_prompt_text), getResources().getColor(R.color.text_problem))),
                            getString(R.string.i_understand),
                            getString(R.string.main_activity_wipe_device_title));
                }
            }
        });

        this.initializedTrezor = null;
        this.connectedTrezorDeviceId = savedInstanceState != null ? savedInstanceState.getString("connectedTrezorDeviceId") : "";
        this.unlockedTimestamp = savedInstanceState != null ? savedInstanceState.getLong("unlockedTimestamp") : Long.MIN_VALUE;
        String lastSelectedDeviceId = gct.getCommonDb().getLastSelectedDeviceId();

        for (InitializedTrezor t : gct.getCommonDb().getTrezors()) {
            if (EqualsUtils.equalsCheckNull(t.getFeatures().getDeviceId(), lastSelectedDeviceId)) {
                this.initializedTrezor = t;
                break;
            }
        }

        //if (TextUtils.isEmpty(connectedTrezorDeviceId))
        //    executeTryConnectTrezor(false);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == GlobalContext.RQC_PICK_HOMESCREEN) {
            if (resultCode == RESULT_OK) {
                ChangeHomescreenActivityResult res = ActivityUtils.getResultParcelable(data);
                executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ApplySettings.newBuilder().setHomescreen(ByteString.copyFrom(res.homescreen)).build());
            }
        }
        else
            super.onActivityResult(requestCode, resultCode, data);
    }

    // pozor - musi byt onResume, kvuli typu aktivity singleTask v manifestu (onResume je vzdy volan po onNewIntent...)
    @Override
    protected void onResume() {
        super.onResume();
        usbPermissionReceiver.register(this);
        navDrawer.trezorListChangedReceiver.register(this);
        navDrawer.trezorListChangedReceiver.onTrezorListChanged(true, true);
        trezorListChangedReceiver.register(this);

        executeTryConnectTrezor(false);
        //if (!TextUtils.isEmpty(connectedTrezorDeviceId))
        //    executeTrezorTaskIfCan(TASK_TRY_CONNECT_TREZOR, GetFeatures.newBuilder().build());
        refreshGui();
    }

    @Override
    protected void onPause() {
        super.onPause();
        usbPermissionReceiver.unregister(this);
        navDrawer.trezorListChangedReceiver.unregister(this);
        trezorListChangedReceiver.register(this);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("connectedTrezorDeviceId", connectedTrezorDeviceId);
        outState.putLong("unlockedTimestamp", unlockedTimestamp);
    }


    //
    // CUSTOM CALLBACKS
    //


    @Override
    public void beforeTrezorTaskCompleted(String id, TrezorTaskResult res) {
        super.beforeTrezorTaskCompleted(id, res);

        if (dialogConfirmAction != null) {
            dialogConfirmAction.dismiss();
            dialogConfirmAction = null;
        }
    }

    @Override
    public void onTrezorTaskCompletedSucc(String id, TrezorTaskResult res) {
        if (id.equals(TASK_TRY_CONNECT_TREZOR)) {
            if (res.getMsgResult().msgType == MessageType.MessageType_Features) {
                Features features = (Features)res.getMsgResult().msg;
                TryConnectTag tag = (TryConnectTag)res.getParam().getTag();

                if (features.getBootloaderMode()) {
                    finish();
                    startActivity(FirmwareUpgradeActivity.createIntent(this, res.getMsgResult()));
                    if (!tag.canAnimateNextScreen)
                        overridePendingTransition(0, 0);
                    setDontDisconnectOnStop();
                }
                else if (gct.getFirmwareReleases(CommonUtils.isTrezorV2(features)).isUpdateRequired(FirmwareVersion.create(features))) {
                    finish();
                    startActivity(FirmwareUpgradeActivity.createIntent(this, res.getMsgResult()));
                    if (!tag.canAnimateNextScreen)
                        overridePendingTransition(0, 0);
                    setDontDisconnectOnStop();
                }

                else if (!features.getInitialized()) {
                    finish();
                    startActivity(InitTrezorActivity.createIntent(this, res.getMsgResult()));
                    if (!tag.canAnimateNextScreen)
                        overridePendingTransition(0, 0);
                    setDontDisconnectOnStop();
                }
                else
                    setupConnectedTrezor(features);
//                    if (this.initializedTrezor != null && (EqualsUtils.equalsCheckNull(this.initializedTrezor.getFeatures().getDeviceId(), features.getDeviceId()))) {
//                        setupConnectedTrezor(features, initializedTrezor.getPublicKey());
//                    }
//                    else {
//                        GetPublicKey msg = GetPublicKey.newBuilder()
//                                .addAddressN((int)2147483692L)
//                                .addAddressN((int)2147483648L)
//                                .addAddressN((int)2147483648L)
//                                .build();
//
//                        executeTrezorTaskIfCan(id, new TrezorTaskParam(msg, new MsgWrp(features)));
//                        refreshGui();
//                    }

            }
            //else if (res.getMsgResult().msgType == MessageType.MessageType_PublicKey) {
            //    setupConnectedTrezor((Features) ((MsgWrp)res.getParam().getTag()).msg, (PublicKey)res.getMsgResult().msg);
            //}
            else if (res.getMsgResult().msgType == MessageType.MessageType_Failure){
                showToastTrezorMsgFailure((Failure)res.getMsgResult().msg);
                showDialogDisconnectTrezor("", getString(R.string.main_activity_disconnect_now));
                refreshGui();
            }
            else
                onTrezorError(TrezorError.ERR_UNEXPECTED_RESPONSE);
        }
        else if (id.startsWith(TASK_CHANGE_SETTING_PREFIX)) {
            if (res.getMsgResult().msgType == MessageType.MessageType_Success) {
                if (id.endsWith(POSTFIX_DISCONNECT_TO_APPLY_SETTING))
                    showDialogDisconnectTrezor("", getString(R.string.main_activity_disconnect_to_apply_setting));
                else if (id.endsWith(POSTFIX_DISCONNECT_WIPED)) {
                    showDialogDisconnectTrezor("", getString(R.string.main_activity_disconnect_now));
                    if (initializedTrezor != null) {
                        gct.getCommonDb().removeTrezor(initializedTrezor);
                    }
                }
                else {
                    executeTryConnectTrezor(true);
                    refreshGui();
                }
            }
            else if (res.getMsgResult().msgType == MessageType.MessageType_ButtonRequest) {
                executeButtonAckAndShowDialog("", getString(R.string.confirm_action_on_trezor), id);
                refreshGui();
            }
            else if (res.getMsgResult().msgType == MessageType.MessageType_Failure) {
                showToastTrezorMsgFailure((Failure)res.getMsgResult().msg);
                refreshGui();
            }
            else
                onTrezorError(TrezorError.ERR_UNEXPECTED_RESPONSE);
        }
        else
            throw new NotImplementedException();
    }

    @Override
    public void onTrezorConnected() {
        super.onTrezorConnected();
        executeTryConnectTrezor(true);
        refreshGui();
    }

    @Override
    public void onTrezorError(ITaskError error) {
        // schvalne nevolam super - komplet vyridim zde

        if (dialogDisconnectTrezor != null) {
            dialogDisconnectTrezor.dismiss();
            dialogDisconnectTrezor = null;
        }

        this.connectedTrezorDeviceId = "";

        if (TrezorError.ERR_NEEDS_PERMISSION.equals(error)) {
            gct.getTrezorManager().requestDevicePermissionIfCan(false);
        }
        else {
            gct.getCommonDb().removeAllTrezors();
            gct.executeDisconnectTrezorTask();

            if (!TrezorError.ERR_NOT_CONNECTED.equals(error))
                error.showToast(gct);
        }

        refreshGui();
    }

    @Override
    public void onPromptDialogDone(String id, boolean canceled, Bundle bundle) {
        if (id.equals(DIALOG_FORGET_DEVICE)) {
            if (!canceled && initializedTrezor != null) {
                if (EqualsUtils.equalsCheckNull(initializedTrezor.getFeatures().getDeviceId(), connectedTrezorDeviceId))
                    showDialogDisconnectTrezor("", getString(R.string.main_activity_disconnect_now));

                gct.getCommonDb().removeTrezor(initializedTrezor);
            }
        }
        else
            throw new NotImplementedException();
    }

    @Override
    public void onEnterTextDialogDone(String id, boolean canceled, String text) {
        if (id.equals(DIALOG_CHANGE_LABEL)) {
            if (!canceled) {
                executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ApplySettings.newBuilder().setLabel(text).build());
                refreshGui();
            }
        }
        else
            throw new NotImplementedException();
    }

    @Override
    public void onCheckConfirmedDialogDone(String id, boolean canceled) {
        if (id.equals(DIALOG_DISABLE_PIN)) {
            if (!canceled) {
                executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ChangePin.newBuilder().setRemove(true).build());
                refreshGui();
            }
        }
        else if (id.equals(DIALOG_DISABLE_PASSPHRASE)) {
            if (!canceled) {
                executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_COMMON, ApplySettings.newBuilder().setUsePassphrase(false).build());
                refreshGui();
            }
        }
        else if (id.equals(DIALOG_WIPE)) {
            if (!canceled) {
                executeTrezorTaskIfCan(TASK_CHANGE_SETTING_PREFIX + POSTFIX_DISCONNECT_WIPED, WipeDevice.newBuilder().build());
                refreshGui();
            }
        }
        else
            throw new NotImplementedException();
    }


    //
    // PRIVATE
    //

    private void setupTwoLinesText(TextView text, int ridTitle, int ritText) {
        text.setText(CustomHtml.fromHtmlWithCustomSpans(getString(ridTitle)
                + CustomHtml.getTextSizeTag("\n" + String.valueOf(CustomHtml.HARD_SPACE) + "\n", ViewUtils.getPixelsFromDp(this, 3))
                + CustomHtml.getTextSizeTag(CustomHtml.getFontColorTag(getString(ritText), getResources().getColor(R.color.text_primary2)), getResources().getDimensionPixelSize(R.dimen.text_body_1))));
    }

    private void setupConnectedTrezor(Features features/*, PublicKey publicKey*/) {
        this.initializedTrezor = new InitializedTrezor(features);
        this.connectedTrezorDeviceId = features.getDeviceId();
        gct.getCommonDb().addOrUpdateCurrentTrezor(initializedTrezor);
        refreshGui();
    }

    private void refreshGui() {
        if (this.dialogDisconnectTrezor != null
                || getTaskFragment().containsAnyTaskByFragmentTag(null))
        {
            progressBar.setVisibility(View.VISIBLE);
            nestedScrollView.setVisibility(View.GONE);
            btnNotification.setVisibility(View.GONE);
        }
        else {
            progressBar.setVisibility(View.GONE);
            nestedScrollView.setVisibility(View.VISIBLE);

            if (initializedTrezor == null) {
                btnNotification.setVisibility(View.GONE);
                rootDisconnected.setVisibility(View.VISIBLE);
                rootConnected.setVisibility(View.GONE);
                getSupportActionBar().setTitle(R.string.app_name);
            }
            else {
                final Features f = initializedTrezor.getFeatures();

                if (f.getNeedsBackup()) {
                    btnNotification.setVisibility(View.VISIBLE);
                    btnNotification.setText(R.string.main_activity_notif_not_backed_up);
                    btnNotification.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startActivity(CreateBackupInitActivity.createIntent(v.getContext(), CommonUtils.isTrezorV2(f)));
                            setDontDisconnectOnStop();
                        }
                    });
                }
                else if (gct.getFirmwareReleases(CommonUtils.isTrezorV2(f)).getNewest().version.isNewerThan(FirmwareVersion.create(f))) {
                    btnNotification.setVisibility(View.VISIBLE);
                    btnNotification.setText(R.string.firmware_upgrade_avail);
                    btnNotification.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startActivity(FirmwareUpgradeActivity.createIntent(v.getContext(), new MsgWrp(f)));
                            setDontDisconnectOnStop();
                        }
                    });
                }
                else if (TextUtils.isEmpty(f.getLabel()) || TextUtils.equals(f.getLabel(), getString(R.string.init_trezor_device_label_default))) {
                    btnNotification.setVisibility(View.VISIBLE);
                    btnNotification.setText(R.string.main_activity_notif_not_named);
                    btnNotification.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startActivity(NameDeviceInitActivity.createIntent(v.getContext(), CommonUtils.isTrezorV2(f)));
                            setDontDisconnectOnStop();
                        }
                    });
                }
                else if (!f.getPinProtection()) {
                    btnNotification.setVisibility(View.VISIBLE);
                    btnNotification.setText(R.string.main_activity_notif_no_pin);
                    btnNotification.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startActivity(SetupPinActivity.createIntent(v.getContext(), CommonUtils.isTrezorV2(f), f.getLabel()));
                            setDontDisconnectOnStop();
                        }
                    });
                }
                else
                    btnNotification.setVisibility(View.GONE);

                rootDisconnected.setVisibility(View.GONE);
                rootConnected.setVisibility(View.VISIBLE);

                getSupportActionBar().setTitle(f.getLabel());
                switchPin.switchCompat.setChecked(f.getPinProtection());
                switchPassphrase.switchCompat.setChecked(f.getPassphraseProtection());
                btnFirmwareVersion.setText(getString(R.string.main_activity_firmware_version).replace("^d^", f.getMajorVersion() + "." + f.getMinorVersion() + "." + f.getPatchVersion()));

                boolean trezorConnected = initializedTrezor != null && EqualsUtils.equalsCheckNull(initializedTrezor.getFeatures().getDeviceId(), connectedTrezorDeviceId);

                btnChangeLabel.setEnabled(trezorConnected);
                btnChangeHomescreen.setEnabled(trezorConnected);
                switchPin.setEnabled(trezorConnected);
                btnChangePin.setEnabled(trezorConnected && f.getPinProtection());
                switchPassphrase.setEnabled(trezorConnected);
                btnFirmwareVersion.setEnabled(trezorConnected);
                btnWipeDevice.setEnabled(trezorConnected);
            }
        }
    }

    private void executeTryConnectTrezor(boolean canAnimateNextScreen) {
        executeTrezorTaskIfCan(TASK_TRY_CONNECT_TREZOR, new TrezorTaskParam(Initialize.newBuilder().build(), new TryConnectTag(canAnimateNextScreen)));
    }

    private void executeButtonAckAndShowDialog(CharSequence dialogTitle, CharSequence dialogText, String taskId) {
        this.dialogConfirmAction = PromptDialog.show(getSupportFragmentManager(), dialogConfirmAction, DIALOG_CONFIRM_ACTION, DIALOG_CONFIRM_ACTION, dialogTitle, dialogText, false, false, false, null, "", "");
        executeTrezorTaskIfCan(taskId, ButtonAck.newBuilder().build());
    }

    private void showDialogDisconnectTrezor(CharSequence dialogTitle, CharSequence dialogText) {
        this.dialogDisconnectTrezor = PromptDialog.show(getSupportFragmentManager(), dialogDisconnectTrezor, DIALOG_DISCONNECT_TREZOR, DIALOG_DISCONNECT_TREZOR, dialogTitle, dialogText, false, false, false, null, "", "");
    }


    private final UsbPermissionReceiver usbPermissionReceiver = new UsbPermissionReceiver() {
        @Override
        public void onUsbPermissionResult(boolean granted) {
            LogUtils.d(TAG, "usbPermissionReceiver: onUsbPermissionResult: granted = " + granted);

            if (granted)
                executeTryConnectTrezor(true);
        }
    };

    private final TrezorListChangedReceiver trezorListChangedReceiver = new TrezorListChangedReceiver() {
        @Override
        public void onTrezorListChanged(boolean listChanged, boolean lastSelectedDeviceIdChanged) {

            if (lastSelectedDeviceIdChanged) {
                String lastDeviceId = gct.getCommonDb().getLastSelectedDeviceId();

                if (!EqualsUtils.equalsCheckNull(lastDeviceId, initializedTrezor != null ? initializedTrezor.getFeatures().getLabel() : "")) {
                    initializedTrezor = null;
                    for (InitializedTrezor t : gct.getCommonDb().getTrezors()) {
                        if (EqualsUtils.equalsCheckNull(t.getFeatures().getDeviceId(), lastDeviceId)) {
                            initializedTrezor = t;
                            break;
                        }
                    }

                    refreshGui();
                }
            }
        }
    };


    //
    // INNER CLASSES
    //

    private static class TryConnectTag extends ApiParcelable {
        public final boolean canAnimateNextScreen;

        public TryConnectTag(boolean canAnimateNextScreen) {
            this.canAnimateNextScreen = canAnimateNextScreen;
        }

        public TryConnectTag(ApiDataInput d) {
            this.canAnimateNextScreen = d.readBoolean();
        }

        @Override
        public void save(ApiDataOutput d, int flags) {
            d.write(this.canAnimateNextScreen);
        }

        public static final ApiCreator<TryConnectTag> CREATOR = new ApiCreator<TryConnectTag>() {
            public TryConnectTag create(ApiDataInput d) { return new TryConnectTag(d); }
            public TryConnectTag[] newArray(int size) { return new TryConnectTag[size]; }
        };
    }

    private static class ViewHolderSwitch {
        public final View button;
        public final TextView text;
        public final SwitchCompat switchCompat;

        public ViewHolderSwitch(View button) {
            this.button = button;

            this.text = (TextView)button.findViewById(R.id.text);
            this.switchCompat = (SwitchCompat)button.findViewById(R.id.switch_compat);
        }

        public void setEnabled(boolean enabled) {
            if (this.button.isEnabled() != enabled) {
                this.button.setEnabled(enabled);
                this.text.setEnabled(enabled);
                this.switchCompat.setEnabled(enabled);
            }
        }
    }
} public class GlobalContext extends GlobalContextLib {
    private static final String TAG = GlobalContext.class.getSimpleName();

    public static final int RQC_ENTER_PIN = 701;
    public static final int RQC_PICK_HOMESCREEN = 702;
    public static final int RQC_PICK_CUSTOM_IMAGE = 703;
    public static final int RQC_ENTER_PASSPHRASE = 704;
    //public static final int RQC_INIT_TREZOR = 705;

    private static final String FIRMWARE_DIR_V1 = "firmware";
    private static final String FIRMWARE_DIR_V2 = "firmware-v2";
    private static final String FIRMWARE_FILE_PREFIX = "trezor-";
    private static final String FIRMWARE_FILE_POSTFIX = ".bin";

    private final TrezorManager trezorManager;

    private CommonDb commonDb;
    private FirmwareReleases firmwareReleases;

    static void init(Context context) {
        GlobalContextLib.init(new GlobalContext(context));
    }

    public static GlobalContext get() {
        return (GlobalContext)GlobalContextLib.get();
    }

    private GlobalContext(Context context) {
        super(context);
        LogUtils.setLoggingEnabled(!getAppIsInProductionMode());
        this.trezorManager = new TrezorManager(context);
    }

    public TrezorManager getTrezorManager() {
        return trezorManager;
    }


    @Override
    public void requestGoogleBackupIfNeeded(String changedDbFileName) {
    }

    @Override
    public String getCurrentLangAbbrev() {
        return Locale.getDefault().getLanguage();
    }

    public synchronized CommonDb getCommonDb() {
        if (this.commonDb == null) {
            LogUtils.d(TAG, "Before creating CommonDb");
            this.commonDb = new CommonDb(this);
            LogUtils.d(TAG, "After creating CommonDb");
        }
        return this.commonDb;
    }


    public synchronized FirmwareReleases getFirmwareReleases(boolean isV2) {
        if (this.firmwareReleases == null || this.firmwareReleases.isV2 != isV2) {
            this.firmwareReleases = null;
            LogUtils.d(TAG, "Before loading releases.json, isV2: " + isV2);
            BufferedReader releasesReader = null;
            final String firmwareDir = isV2 ? FIRMWARE_DIR_V2 : FIRMWARE_DIR_V1;

            try {
                releasesReader = new BufferedReader(new InputStreamReader(getAndroidContext().getAssets().open(firmwareDir + File.separatorChar + "releases.json", AssetManager.ACCESS_BUFFER), "UTF-8"));
                StringBuilder bRelease = new StringBuilder();

                String line;
                while ((line = releasesReader.readLine()) != null) {
                    bRelease.append(line).append('\n');
                }

                JSONArray jReleases = new JSONArray(bRelease.toString());
                ArrayList<FirmwareRelease> versions = new ArrayList<>();

                for (int i = 0; i < jReleases.length(); i++) {
                    JSONObject jRelease = jReleases.getJSONObject(i);
                    JSONArray jVersion = jRelease.getJSONArray("version");

                    versions.add(new FirmwareRelease(
                            jRelease.getBoolean("required"),
                            new FirmwareVersion(jVersion.getInt(0), jVersion.getInt(1), jVersion.getInt(2)),
                            jRelease.optString("fingerprint", ""),
                            jRelease.getString("changelog")));
                }

                Collections.sort(versions, Collections.reverseOrder());
                this.firmwareReleases = new FirmwareReleases(isV2, ImmutableList.copyOf(versions));
            }
            catch (Exception e) {
                LogUtils.e(TAG, "getBundledFirmwareVersion", e);
                this.firmwareReleases = FirmwareReleases.createInvalid(isV2);
            }
            finally {
                if (releasesReader != null) {
                    try {
                        releasesReader.close();
                    } catch (IOException e) {
                        LogUtils.e(TAG, "getBundledFirmwareVersion (closing releaseReader)", e);
                    }
                }
            }
            LogUtils.d(TAG, "After loading bundled firmware version");
        }
        return this.firmwareReleases;
    }

    public synchronized InputStream openStreamBundledFirmware(boolean isV2) throws IOException {
        FirmwareRelease f = getFirmwareReleases(isV2).getNewest();
        return getAndroidContext().getAssets().open((isV2 ? FIRMWARE_DIR_V2 : FIRMWARE_DIR_V1) + File.separatorChar + FIRMWARE_FILE_PREFIX + f.version.major + "." + f.version.minor + "." + f.version.patch + FIRMWARE_FILE_POSTFIX);
    }

    public synchronized void executeDisconnectTrezorTask() {
        // schvalne resime takto globalne pres taskExecutor, protoze kazdopadne nechceme, aby se task (cekajici ve fronte) zrusil napr. pri zavreni aktivity...
        getTaskExecutor().executeTask("TASK_DISCONNECT_TREZOR_GCT",
                new AsyncTaskParam(TrezorTaskParam.SERIAL_EXECUTION_KEY_TREZOR) {
                    @Override
                    public boolean isExecutionInParallelForbidden(ITaskContext context) {
                        return true;
                    }

                    @Override
                    public void execute(ITaskContext context, ITask task) throws TaskException {
                        getTrezorManager().closeDeviceConnection();
                    }
                },
                null,
                false,
                new EmptyTaskListener(),
                null);
    }


    public static class FirmwareReleases {
        public static final FirmwareReleases createInvalid(boolean isV2) {
            return new FirmwareReleases(isV2, ImmutableList.<FirmwareRelease>of());
        }

        public final boolean isV2;
        public final ImmutableList<FirmwareRelease> releasesDesc; // sorted from the newest to oldest

        // upraveno: versionsDesc
        public FirmwareReleases(boolean isV2, ImmutableList<FirmwareRelease> releasesDesc) {
            this.isV2 = isV2;
            this.releasesDesc = releasesDesc;
        }

        public FirmwareRelease getNewest() {
            return releasesDesc.size() > 0 ? releasesDesc.get(0) : null;
        }

        public boolean isUpdateRequired(FirmwareVersion deviceVersion) {
            if (!deviceVersion.isValid())
                return false;

            for (FirmwareRelease r : releasesDesc) {
                if (!r.version.isNewerThan(deviceVersion))
                    return false;

                if (r.required)
                    return true;
            }
            return false;
        }
    }

    public static class FirmwareRelease implements Comparable<FirmwareRelease> {
        public final boolean required;
        public final FirmwareVersion version;
        public final String fingerprint;
        public final String changelog;

        public FirmwareRelease(boolean required, FirmwareVersion version, String fingerprint, String changelog) {
            this.required = required;
            this.version = version;
            this.fingerprint = fingerprint;
            this.changelog = changelog;
        }

        @Override
        public int compareTo(@NonNull FirmwareRelease v) {
            return version.compareTo(v.version);
        }

        @Override
        public String toString() {
            return version.toString();
        }
    }

    public static class FirmwareVersion implements Comparable<FirmwareVersion> {
        public static final FirmwareVersion INVALID = new FirmwareVersion(0, 0, 0);

        public final int major;
        public final int minor;
        public final int patch;

        public static FirmwareVersion create(Features features) {
            if (features.getBootloaderMode()) {
                return (features.hasFwMajor() && features.hasFwMinor() && features.hasFwPatch() && features.getFwMajor() > 0) ?
                        new FirmwareVersion(features.getFwMajor(), features.getFwMinor(), features.getFwPatch()) : FirmwareVersion.INVALID;
            }
            else
                return new FirmwareVersion(features.getMajorVersion(), features.getMinorVersion(), features.getPatchVersion());
        }

        public FirmwareVersion(int major, int minor, int patch) {
            this.major = major;
            this.minor = minor;
            this.patch = patch;
        }

        public boolean isValid() {
            return major != 0 || minor != 0 || patch != 0;
        }

        public boolean isNewerThan(FirmwareVersion v) {
            return compareTo(v) > 0;
        }

        @Override
        public int compareTo(@NonNull FirmwareVersion v) {
            if (this.major != v.major)
                return this.major < v.major ? -1 : 1;
            else if (this.minor != v.minor)
                return this.minor < v.minor ? -1 : 1;
            else if (this.patch != v.patch)
                return this.patch < v.patch ? -1 : 1;
            else
                return 0;
        }

        @Override
        public String toString() {
            return major + "." + minor + "." + patch;
        }
    }
} public interface INavDrawerCallbacks {
    void onActivityStart();
    void onActivityStop();
} public static class TrezorError extends TaskError {
    public static final TrezorError ERR_NOT_CONNECTED = new TrezorError(TrezorException.TYPE_NOT_CONNECTED, R.string.trezor_err_not_connected);
    public static final TrezorError ERR_NEEDS_PERMISSION = new TrezorError(TrezorException.TYPE_NEEDS_PERMISSION, R.string.trezor_err_needs_permission);
    public static final TrezorError ERR_COMMUNICATION_ERROR = new TrezorError(TrezorException.TYPE_COMMUNICATION_ERROR, R.string.trezor_err_communication_error);
    public static final TrezorError ERR_UNEXPECTED_RESPONSE = new TrezorError(TrezorException.TYPE_UNEXPECTED_RESPONSE, R.string.trezor_err_unexpected_response);
    // POZOR pri pridavani nezapominat pridavat case radky do tryGetTrezorError()

    private static TrezorError tryGetTrezorError(int type) {
        switch (type) {
            case TrezorException.TYPE_NOT_CONNECTED: return ERR_NOT_CONNECTED;
            case TrezorException.TYPE_NEEDS_PERMISSION: return ERR_NEEDS_PERMISSION;
            case TrezorException.TYPE_COMMUNICATION_ERROR: return ERR_COMMUNICATION_ERROR;
            case TrezorException.TYPE_UNEXPECTED_RESPONSE: return ERR_UNEXPECTED_RESPONSE;
            default: return null;
        }
    }


    private final int type;
    private final int msgRid;

    public static ITaskError getErrorFromException(TrezorException ex) {
        TrezorError res = tryGetTrezorError(ex.getType());
        return res != null ? res : BaseError.ERR_UNKNOWN_ERROR;
    }

    // private!
    private TrezorError(int type, int msgRid) {
        this.type = type;
        this.msgRid = msgRid;
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.type);
    }

    public int getType() {
        return this.type;
    }

    public int getMsgRid() {
        return this.msgRid;
    }

    @Override
    public int hashCode() {
        int _hash = 17;
        _hash = _hash * 29 + type;
        _hash = _hash * 29 + msgRid;
        return _hash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof TrezorError)) {
            return false;
        }

        TrezorError lhs = (TrezorError) o;
        return lhs != null &&
                type == lhs.type &&
                msgRid == lhs.msgRid;
    }

    @Override
    public CharSequence getMsg(ITaskContext context) {
        return context.getAndroidContext().getString(msgRid);
    }

    @Override
    public String getGoogleAnalyticsId() {
        return "TrezorError:" + type;
    }

    // upraveno!
    public static final ApiCreator<TrezorError> CREATOR = new ApiCreator<TrezorError>() {
        public TrezorError create(ApiDataInput d) {
            TrezorError ret = tryGetTrezorError(d.readInt());
            if (ret == null)
                throw new IllegalStateException();
            return ret;
        }

        public TrezorError[] newArray(int size) { return new TrezorError[size]; }
    };
} public static class TrezorTaskParam extends TaskParam implements ITrezorTaskParam {
    private final MsgWrp msgParam;
    private final IApiParcelable tag; // abstract optional

    public TrezorTaskParam(Message msg) {
        this(new MsgWrp(msg), null);
    }

    public TrezorTaskParam(Message msg, IApiParcelable tag) {
        this(new MsgWrp(msg), tag);
    }

    public TrezorTaskParam(MsgWrp msgParam, IApiParcelable tag) {
        this.msgParam = msgParam;
        this.tag = tag;
    }

    public TrezorTaskParam(ApiDataInput d) {
        this.msgParam = d.readObject(MsgWrp.CREATOR);
        this.tag = d.readOptParcelableWithName();
    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.write(this.msgParam, flags);
        d.writeOpt(this.tag, flags);
    }

    public MsgWrp getMsgParam() {
        return this.msgParam;
    }

    public IApiParcelable getTag() {
        return this.tag;
    }

    @Override
    public int describeContents() {
        return ApiParcelable.baseDescribeContents();
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        ApiParcelable.baseWriteToParcel(this, parcel, i);
    }

    @Override
    public String getSerialExecutionKey(ITaskContext context) {
        return SERIAL_EXECUTION_KEY_TREZOR;
    }

    @Override
    public boolean isExecutionInParallelForbidden(ITaskContext context) {
        return true;
    }

    @Override
    public TrezorTaskResult createResultUncached(ITaskContext context, ITask task) throws TaskException {
        try {
            GlobalContext gct = (GlobalContext)context;
            TrezorManager t = gct.getTrezorManager();

            if (!t.tryConnectDevice()) {
                return createErrorResult(context, task, t.hasDeviceWithoutPermission(false) ? TrezorError.ERR_NEEDS_PERMISSION : TrezorError.ERR_NOT_CONNECTED);
            }
            else {
                MsgWrp msgResult = new MsgWrp(t.sendMessage(msgParam.msg));
                return new TrezorTaskResult(this, BaseError.ERR_OK, msgResult);
            }
        }
        catch (TrezorException ex) {
            return createErrorResult(context, task, TrezorError.getErrorFromException(ex));
        }
    }

    @Override
    public TrezorTaskResult createErrorResult(ITaskContext context, ITask task, ITaskError error) {
        return new TrezorTaskResult(this, error, null);
    }

    public static final ApiCreator<TrezorTaskParam> CREATOR = new ApiCreator<TrezorTaskParam>() {
        public TrezorTaskParam create(ApiDataInput d) { return new TrezorTaskParam(d); }
        public TrezorTaskParam[] newArray(int size) { return new TrezorTaskParam[size]; }
    };
} public static class TrezorTaskResult extends TaskResult<TrezorTaskParam> implements ITrezorTaskResult {
    private final MsgWrp msgResult;

    public TrezorTaskResult(TrezorTaskParam param, ITaskError error, MsgWrp msgResult) {
        super(param, error);
        this.msgResult = msgResult;
    }

    public TrezorTaskResult(ApiDataInput d) {
        super(d.<TrezorTaskParam>readParcelableWithName(), d.<ITaskError>readParcelableWithName());

        if (isValidResult()) {
            this.msgResult = d.readObject(MsgWrp.CREATOR);
        }
        else {
            this.msgResult = null;
        }

    }

    @Override
    public void save(ApiDataOutput d, int flags) {
        d.writeWithName(getParam(), flags);
        d.writeWithName(getError(), flags);

        if (isValidResult()) {
            d.write(this.msgResult, flags);
        }
    }

    public MsgWrp getMsgResult() {
        return this.msgResult;
    }

    @Override
    public int describeContents() {
        return ApiParcelable.baseDescribeContents();
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        ApiParcelable.baseWriteToParcel(this, parcel, i);
    }

    @Override
    public boolean isCacheableResult() {
        return false;
    }

    public static final ApiCreator<TrezorTaskResult> CREATOR = new ApiCreator<TrezorTaskResult>() {
        public TrezorTaskResult create(ApiDataInput d) { return new TrezorTaskResult(d); }
        public TrezorTaskResult[] newArray(int size) { return new TrezorTaskResult[size]; }
    };
} public static abstract class TrezorConnectionChangedReceiver extends BaseBroadcastReceivers.BaseGlobalReceiver {
    private static final String TAG = TrezorConnectionChangedReceiver.class.getSimpleName();

    private static IntentFilter createIntentFilter() {
        IntentFilter ret = new IntentFilter();
        ret.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        ret.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        return ret;
    }

    public TrezorConnectionChangedReceiver() {
        super(createIntentFilter());
    }

    @Override
    protected final void onReceiveRegistered(Context context, Intent intent) {
        Timber.d(TAG, "onReceiveRegistered: " + intent.getAction());

        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
        if (device != null && deviceIsTrezor(device)) {
            if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction()))
                onTrezorConnectionChanged(true);
            else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) {
                onTrezorConnectionChanged(false);
            }
        }
    }

    public abstract void onTrezorConnectionChanged(boolean connected);
}
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.Toast;
import com.circlegate.liban.dialog.PromptDialog;
import com.satoshilabs.trezor.lib.BaseBroadcastReceivers.BaseLocalReceiver;
import com.circlegate.liban.dialog.DialogsFragment;
import com.circlegate.liban.dialog.DialogsFragment.IDialogsFragmentActivity;
import com.circlegate.liban.fragment.BaseFragmentCommon.IBaseFragmentActivity;
import com.circlegate.liban.fragment.BaseFragmentCommon.OnBackPressedListener;
import com.circlegate.liban.task.TaskErrors.ITaskError;
import com.circlegate.liban.task.TaskFragment;
import com.circlegate.liban.task.TaskFragment.ITaskFragmentActivity;
import com.circlegate.liban.task.TaskInterfaces.ITaskResult;
import com.circlegate.liban.task.TaskInterfaces.ITaskResultListener;
import com.circlegate.liban.utils.ActivityUtils;
import com.circlegate.liban.utils.EqualsUtils;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import com.satoshilabs.trezor.app.R;
import com.satoshilabs.trezor.app.activity.EnterPassphraseActivity;
import com.satoshilabs.trezor.app.activity.EnterPassphraseActivity.EnterPassphraseActivityParam;
import com.satoshilabs.trezor.app.activity.EnterPassphraseActivity.EnterPassphraseActivityResult;
import com.satoshilabs.trezor.app.activity.EnterPinActivity;
import com.satoshilabs.trezor.app.activity.EnterPinActivity.EnterPinActivityParam;
import com.satoshilabs.trezor.app.activity.EnterPinActivity.EnterPinActivityResult;
import com.satoshilabs.trezor.app.activity.MainActivity;
import com.satoshilabs.trezor.app.common.GlobalContext;
import com.satoshilabs.trezor.app.common.NavDrawer.INavDrawerCallbacks;
import com.satoshilabs.trezor.app.common.TrezorTasks.TrezorError;
import com.satoshilabs.trezor.app.common.TrezorTasks.TrezorTaskParam;
import com.satoshilabs.trezor.app.common.TrezorTasks.TrezorTaskResult;
import com.satoshilabs.trezor.lib.TrezorManager.TrezorConnectionChangedReceiver;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.ButtonAck;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.Cancel;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.EntropyAck;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.Failure;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.PassphraseAck;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.PinMatrixAck;
import com.satoshilabs.trezor.lib.protobuf.TrezorMessage.PinMatrixRequest;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
package com.satoshilabs.trezor.app.activity.base;




public abstract class BaseActivity extends AppCompatActivity implements IBaseFragmentActivity, ITaskFragmentActivity, IDialogsFragmentActivity, OnRequestPermissionsResultCallback, ITaskResultListener {
    private static final String DIALOG_CONFIRM_ACTION = "BaseActivity.DIALOG_CONFIRM_ACTION";

    private GlobalContext gct;
    private TaskFragment taskFragment;
	private DialogsFragment dialogsFragment;
    private final ArrayList<OnBackPressedListener> onBackPressedListeners = new ArrayList<>();
    private final List<Runnable> pendingTasks = new ArrayList<Runnable>();
    private boolean readyToCommitFragments = false;
    private INavDrawerCallbacks navDrawerCallbacks;

    // pokud je false a pokud nedochazi pouze ke zmene konfigurace (nejcasteji kvuli otoceni displeje), pri kazdem volani onStop dojde k odpojeni trezoru
    // aktivita muze nastavit na true, aby pri prechodu z jedne aktivity do druhe nedochazelo ke zbytecnemu odpojovani
    // V kazdem pripade pri kazdem volani onStop (a onCreate) je nastaveno zpet na false
    private boolean dontDisconnectOnStop = false;

    // Saved state
    private PromptDialog dialogConfirmAction;


    //
    // LIFECYCLE CALLBACKS
    //

	@Override
	protected void onCreate(Bundle savedInstanceState) {
	    this.gct = GlobalContext.get();
        super.onCreate(savedInstanceState);
        onBackPressedListeners.clear(); // pro jistotu...
        readyToCommitFragments = false;
        trezorConnectionChangedReceiver.register(this);
        dontDisconnectOnStop = false;

        this.dialogConfirmAction = (PromptDialog)getSupportFragmentManager().findFragmentByTag(DIALOG_CONFIRM_ACTION);
	}

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == GlobalContext.RQC_ENTER_PIN) {
            EnterPinActivityResult res = ActivityUtils.getResultParcelable(data);
            executeTrezorTaskIfCan(
                    res.param.taskId,
                    new TrezorTaskParam(resultCode == RESULT_OK ? PinMatrixAck.newBuilder().setPin(res.pinEncoded).build() : Cancel.newBuilder().build(), res.param.tag));
        }
        else if (requestCode == GlobalContext.RQC_ENTER_PASSPHRASE) {
            EnterPassphraseActivityResult res = ActivityUtils.getResultParcelable(data);