From 855109450e7a1b8c79d6e0af05c097a590350390 Mon Sep 17 00:00:00 2001
From: Francesco Andreuzzi <andreuzzi.francesco@gmail.com>
Date: Sat, 17 Dec 2016 23:28:30 +0100
Subject: [PATCH] 4.8

---
 app/build.gradle                              | 14 +---
 .../consolelauncher/LauncherActivity.java     | 73 ++++++++++++++-----
 .../andre/consolelauncher/MainManager.java    |  8 +-
 .../ohi/andre/consolelauncher/UIManager.java  | 34 ++++++---
 .../consolelauncher/commands/ExecInfo.java    |  4 +
 .../consolelauncher/commands/raw/call.java    | 20 ++++-
 .../consolelauncher/commands/raw/flash.java   | 12 +++
 .../managers/ContactManager.java              | 11 +++
 .../managers/PreferencesManager.java          | 11 ++-
 .../managers/TerminalMAnager.java             | 64 +++++++++++-----
 app/src/main/res/raw/settings.txt             |  3 +-
 app/src/main/res/values/strings.xml           | 12 +--
 12 files changed, 193 insertions(+), 73 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index fdc7b15..b9402d1 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,13 +1,5 @@
 apply plugin: 'com.android.application'
 android {
-    signingConfigs {
-        config {
-            keyAlias 'mykey'
-            keyPassword 'Dodici12'
-            storeFile file('/Users/francescoandreuzzi/Programming/Android/Build/my-release-key.keystore')
-            storePassword 'Dodici12'
-        }
-    }
 
     compileSdkVersion 25
     buildToolsVersion '25.0.1'
@@ -15,8 +7,8 @@ android {
         applicationId "ohi.andre.consolelauncher"
         minSdkVersion 8
         targetSdkVersion 23
-        versionCode 77
-        versionName "4.7"
+        versionCode 78
+        versionName "4.8"
     }
 
     buildTypes {
@@ -39,6 +31,4 @@ android {
         compile 'commons-io:commons-io:2.4'
     }
 
-}
-dependencies {
 }
\ No newline at end of file
diff --git a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
index a2aa5e6..a1ebd2c 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
@@ -1,7 +1,6 @@
 package ohi.andre.consolelauncher;
 
 import android.Manifest;
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -12,15 +11,18 @@ import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
 import android.support.v4.content.ContextCompat;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.widget.Toast;
 
 import java.io.File;
 import java.io.IOException;
 
+import ohi.andre.consolelauncher.commands.ExecInfo;
 import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
 import ohi.andre.consolelauncher.tuils.interfaces.Inputable;
 import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
@@ -33,6 +35,10 @@ public class LauncherActivity extends Activity implements Reloadable {
 
     private final int FILEUPDATE_DELAY = 300;
 
+    public static final int COMMAND_REQUEST_PERMISSION = 10;
+    public static final int STORAGE_PERMISSION = 11;
+    public static final int COMMAND_SUGGESTION_REQUEST_PERMISSION = 12;
+
     private final String FIRSTACCESS_KEY = "firstAccess";
 
     private UIManager ui;
@@ -91,9 +97,6 @@ public class LauncherActivity extends Activity implements Reloadable {
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        DevicePolicyManager policy = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
-        ComponentName component = new ComponentName(this, PolicyReceiver.class);
-
         SharedPreferences preferences = getPreferences(0);
         boolean firstAccess = preferences.getBoolean(FIRSTACCESS_KEY, true);
         if (firstAccess) {
@@ -104,13 +107,28 @@ public class LauncherActivity extends Activity implements Reloadable {
             Tuils.showTutorial(this);
         }
 
-        Resources res = getResources();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
-            checkPermission(res);
-        if (isFinishing())
+        if (isFinishing()) {
             return;
+        }
+
+        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (!(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED  &&
+                    ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
 
+                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, LauncherActivity.STORAGE_PERMISSION);
+                return;
+            }
+        }
+
+        finishOnCreate();
+    }
+
+    private void finishOnCreate() {
         File tuiFolder = getFolder();
+        Resources res = getResources();
+
+        DevicePolicyManager policy = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+        ComponentName component = new ComponentName(this, PolicyReceiver.class);
 
         try {
             preferencesManager = new PreferencesManager(res.openRawResource(R.raw.settings), res.openRawResource(R.raw.alias), tuiFolder);
@@ -173,14 +191,6 @@ public class LauncherActivity extends Activity implements Reloadable {
             getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
     }
 
-    @TargetApi(23)
-    private void checkPermission(Resources res) {
-        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
-            this.finish();
-            Tuils.openSettingsPage(this, res.getString(R.string.permissions_toast));
-        }
-    }
-
     @Override
     protected void onStart() {
         super.onStart();
@@ -244,8 +254,37 @@ public class LauncherActivity extends Activity implements Reloadable {
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
 
-        if (hasFocus)
+        if (hasFocus) {
             hideStatusBar();
+            ui.focusTerminal();
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
+        switch (requestCode) {
+            case COMMAND_REQUEST_PERMISSION:
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    ExecInfo info = main.getInfo();
+                    main.onCommand(info.calledCommand, info.calledCommandOutputId);
+                } else {
+                    ui.setOutput(getString(R.string.output_nopermissions), main.getInfo().calledCommandOutputId);
+                }
+                break;
+            case STORAGE_PERMISSION:
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    finishOnCreate();
+                } else {
+                    Toast.makeText(this, R.string.permissions_toast, Toast.LENGTH_LONG).show();
+                    finish();
+                }
+                break;
+            case COMMAND_SUGGESTION_REQUEST_PERMISSION:
+                if (grantResults.length == 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+                    ui.setOutput(getString(R.string.output_nopermissions), main.getInfo().calledCommandOutputId);
+                }
+                break;
+        }
     }
 
 }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
index dc1964d..8bc0c6c 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
@@ -238,6 +238,9 @@ public class MainManager {
                 public void run() {
                     super.run();
 
+                    info.calledCommand = input;
+                    info.calledCommandOutputId = id;
+
                     try {
                         Command command = CommandTuils.parse(input, info, false);
 
@@ -247,7 +250,10 @@ public class MainManager {
                         }
 
                         if (returnValue[0]) {
-                            out.onOutput(command.exec(info), id);
+                            String output = command.exec(info);
+                            if(output != null) {
+                                out.onOutput(output, id);
+                            }
                         }
                     } catch (Exception e) {
                         out.onOutput(e.toString(), id);
diff --git a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
index b4c6ad3..877cf04 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
@@ -43,6 +43,7 @@ import ohi.andre.consolelauncher.managers.PreferencesManager;
 import ohi.andre.consolelauncher.managers.SkinManager;
 import ohi.andre.consolelauncher.managers.SuggestionsManager;
 import ohi.andre.consolelauncher.managers.TerminalManager;
+import ohi.andre.consolelauncher.tuils.ShellUtils;
 import ohi.andre.consolelauncher.tuils.SuggestionRunnable;
 import ohi.andre.consolelauncher.tuils.Tuils;
 import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
@@ -358,7 +359,7 @@ public class UIManager implements OnTouchListener {
             deviceInfo = null;
         }
 
-        boolean inputBottom = Boolean.parseBoolean(prefsMgr.getValue(PreferencesManager.INPUTFIELD_BOTTOM));
+        final boolean inputBottom = Boolean.parseBoolean(prefsMgr.getValue(PreferencesManager.INPUTFIELD_BOTTOM));
         int layoutId = inputBottom ? R.layout.input_down_layout : R.layout.input_up_layout;
 
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -419,8 +420,7 @@ public class UIManager implements OnTouchListener {
         } else
             initDetector();
 
-        mTerminalAdapter = new TerminalManager(terminalView, inputView, prefixView, submitView, skinManager, getHint(prefsMgr),
-                Boolean.parseBoolean(prefsMgr.getValue(PreferencesManager.ENTER_PHYSICAL_KEYBOARD)));
+        mTerminalAdapter = new TerminalManager(terminalView, inputView, prefixView, submitView, skinManager, getHint(prefsMgr), inputBottom);
         mTerminalAdapter.setInputListener(new OnNewInputListener() {
             @Override
             public void onNewInput(String input) {
@@ -430,6 +430,9 @@ public class UIManager implements OnTouchListener {
                 trigger.exec(input, mTerminalAdapter.getCurrentOutputId());
             }
         });
+//        if(Boolean.parseBoolean(prefsMgr.getValue(PreferencesManager.SHOW_DONATE_MESSAGE))) {
+//            mTerminalAdapter.addMessager(new TerminalManager.Messager(65, context.getString(R.string.rate_donate_text)));
+//        }
 
         ViewTreeObserver observer = rootView.getViewTreeObserver();
         final TextView device = deviceInfo;
@@ -449,7 +452,8 @@ public class UIManager implements OnTouchListener {
                 int terminalHeight = rootHeight - deviceHeight - ramHeight - inputHeight - suggestionHeight;
 
                 View parent = (View) terminalView.getParent();
-                parent.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, terminalHeight));
+                parent.setLayoutParams(inputBottom ? new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, terminalHeight) :
+                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, terminalHeight));
             }
         });
     }
@@ -495,6 +499,10 @@ public class UIManager implements OnTouchListener {
         ram.setText("RAM: " + Tuils.ramDetails(activityManager, memory));
     }
 
+    public void focusTerminal() {
+        mTerminalAdapter.requestInputFocus();
+    }
+
     //	 get hint for input
     private String getHint(PreferencesManager preferencesManager) {
         boolean showUsername = Boolean.parseBoolean(preferencesManager.getValue(PreferencesManager.SHOWUSERNAME));
@@ -534,15 +542,19 @@ public class UIManager implements OnTouchListener {
 
             @Override
             public boolean onDoubleTap(MotionEvent e) {
-                boolean admin = policy.isAdminActive(component);
+                if(Tuils.verifyRoot()) {
+                    ShellUtils.execCommand("input keyevent 26", true, null);
+                    return true;
+                } else {
+                    boolean admin = policy.isAdminActive(component);
 
-                if (!admin)
-                    Tuils.requestAdmin((Activity) mContext, component,
-                            mContext.getString(R.string.adminrequest_label));
-                else
-                    policy.lockNow();
+                    if (!admin)
+                        Tuils.requestAdmin((Activity) mContext, component, mContext.getString(R.string.adminrequest_label));
+                    else
+                        policy.lockNow();
 
-                return true;
+                    return true;
+                }
             }
         });
     }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/ExecInfo.java b/app/src/main/java/ohi/andre/consolelauncher/commands/ExecInfo.java
index ed2631b..c79c90f 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/ExecInfo.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/ExecInfo.java
@@ -82,6 +82,10 @@ public class ExecInfo {
     //	uses su
     private boolean canUseSu = false;
 
+//    output when permission is needed
+    public String calledCommand;
+    public int calledCommandOutputId;
+
     public ExecInfo(Context context, PreferencesManager prefsMgr, CommandGroup commandGroup, AliasManager alMgr, AppsManager appmgr, MusicManager p,
                     ContactManager c, DevicePolicyManager devicePolicyManager, ComponentName componentName,
                     Reloadable r, Runnable cl, CommandExecuter executeCommand) {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/call.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/call.java
index 2d1222f..6238211 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/call.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/call.java
@@ -1,13 +1,16 @@
 package ohi.andre.consolelauncher.commands.raw;
 
 import android.Manifest;
+import android.app.Activity;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.support.v4.app.ActivityCompat;
 import android.support.v4.content.ContextCompat;
 
 import java.util.List;
 
+import ohi.andre.consolelauncher.LauncherActivity;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecInfo;
@@ -18,7 +21,15 @@ public class call implements CommandAbstraction {
     @Override
     public String exec(ExecInfo info) {
         if (ContextCompat.checkSelfPermission(info.context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
-            return info.res.getString(R.string.output_nopermissions);
+
+            ActivityCompat.requestPermissions((Activity) info.context, new String[]{Manifest.permission.READ_CONTACTS}, LauncherActivity.COMMAND_REQUEST_PERMISSION);
+            return info.context.getString(R.string.output_waitingpermission);
+        }
+
+        if (ContextCompat.checkSelfPermission(info.context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
+
+            ActivityCompat.requestPermissions((Activity) info.context, new String[]{Manifest.permission.CALL_PHONE}, LauncherActivity.COMMAND_REQUEST_PERMISSION);
+            return info.context.getString(R.string.output_waitingpermission);
         }
 
         String number = info.get(String.class, 0);
@@ -28,7 +39,7 @@ public class call implements CommandAbstraction {
         try {
             info.context.startActivity(intent);
         } catch (SecurityException e) {
-            return info.res.getString(R.string.permission_error);
+            return info.res.getString(R.string.output_nopermissions);
         }
 
         return info.res.getString(R.string.calling) + " " + number;
@@ -67,11 +78,12 @@ public class call implements CommandAbstraction {
     @Override
     public String onNotArgEnough(ExecInfo info, int nArgs) {
         if (ContextCompat.checkSelfPermission(info.context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
-            return info.res.getString(R.string.output_nopermissions);
+            ActivityCompat.requestPermissions((Activity) info.context, new String[]{Manifest.permission.READ_CONTACTS}, LauncherActivity.COMMAND_REQUEST_PERMISSION);
+            return info.context.getString(R.string.output_waitingpermission);
         }
 
         List<String> contacts = info.contacts.listNamesAndNumbers();
-        Tuils.addPrefix(contacts, "  ");
+        Tuils.addPrefix(contacts, Tuils.DOUBLE_SPACE);
         Tuils.insertHeaders(contacts, false);
         return Tuils.toPlanString(contacts);
     }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/flash.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/flash.java
index c368bb1..14372a3 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/flash.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/flash.java
@@ -1,10 +1,17 @@
 package ohi.andre.consolelauncher.commands.raw;
 
+import android.Manifest;
+import android.app.Activity;
+import android.content.pm.PackageManager;
 import android.hardware.Camera.Parameters;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
 
+import ohi.andre.consolelauncher.LauncherActivity;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecInfo;
+import ohi.andre.consolelauncher.tuils.Tuils;
 
 @SuppressWarnings("deprecation")
 public class flash implements CommandAbstraction {
@@ -14,6 +21,11 @@ public class flash implements CommandAbstraction {
         if (!info.canUseFlash)
             return info.res.getString(R.string.output_flashlightnotavailable);
 
+        if (ContextCompat.checkSelfPermission(info.context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+            ActivityCompat.requestPermissions((Activity) info.context, new String[]{Manifest.permission.CAMERA}, LauncherActivity.COMMAND_REQUEST_PERMISSION);
+            return info.context.getString(R.string.output_waitingpermission);
+        }
+
         final ExecInfo execInfo = info;
         new Thread() {
             @Override
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
index bae6669..9aae09f 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
@@ -1,8 +1,13 @@
 package ohi.andre.consolelauncher.managers;
 
+import android.Manifest;
+import android.app.Activity;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.provider.ContactsContract;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
 
 import java.util.ArrayList;
 import java.util.Map;
@@ -11,6 +16,7 @@ import java.util.Set;
 import java.util.TreeMap;
 
 import ohi.andre.comparestring.Compare;
+import ohi.andre.consolelauncher.LauncherActivity;
 
 public class ContactManager {
 
@@ -25,6 +31,11 @@ public class ContactManager {
     private Map<String, String> getContacts() {
         Map<String, String> contacts = new TreeMap<>();
 
+        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
+            ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_CONTACTS}, LauncherActivity.COMMAND_SUGGESTION_REQUEST_PERMISSION);
+            return contacts;
+        }
+
         Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
         while (phones != null && phones.moveToNext()) {
             String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java
index 47b6a36..5575e48 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java
@@ -1,6 +1,7 @@
 package ohi.andre.consolelauncher.managers;
 
 import android.annotation.SuppressLint;
+import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -57,8 +58,8 @@ public class PreferencesManager {
     public static final String FULLSCREEN = "fullscreen";
     public static final String NOTIFICATION = "keepAliveWithNotification";
     public static final String OPEN_KEYBOARD = "openKeyboardOnStart";
-    public static final String ENTER_PHYSICAL_KEYBOARD = "enableEnterInPhysicalKeyboard";
     public static final String COMPARESTRING_APPS = "compareStringForApps";
+    public static final String SHOW_DONATE_MESSAGE = "showDonationMessage";
 
     public static final String ALIAS_FILENAME = "alias.txt";
     public static final int ALIAS = 11;
@@ -228,6 +229,10 @@ public class PreferencesManager {
 
     @SuppressLint("DefaultLocale")
     private String getValue(List<String> values, String key) {
+        if(values == null) {
+            return null;
+        }
+
         for (String s : values) {
             String k = obtainKey(s);
             if (k != null && k.equals(key))
@@ -240,6 +245,8 @@ public class PreferencesManager {
     private List<String> readAllInput(InputStream i) {
         BufferedReader br = null;
 
+        Log.e("andre", i.toString());
+
         List<String> list = new ArrayList<>();
         String line;
         try {
@@ -248,7 +255,7 @@ public class PreferencesManager {
                 list.add(line);
 
         } catch (IOException e) {
-            e.printStackTrace();
+            Log.e("andre", "", e);
         } finally {
             if (br != null)
                 try {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
index 8205f18..30b8caa 100644
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
@@ -7,8 +7,10 @@ import android.text.InputType;
 import android.text.Layout;
 import android.text.Spannable;
 import android.text.SpannableString;
+import android.text.TextUtils;
 import android.text.method.ScrollingMovementMethod;
 import android.text.style.ForegroundColorSpan;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
@@ -48,6 +50,7 @@ public class TerminalManager {
     public static final int OUTPUT = 11;
 
     private int mCurrentOutputId = 0;
+    private int globalId = 0;
 
     private ScrollView mScrollView;
     private TextView mTerminalView;
@@ -63,13 +66,14 @@ public class TerminalManager {
     };
     private SkinManager mSkinManager;
 
-    private String originalInput;
-    private List<InputText> propertyText = new ArrayList<>();
-
     private OnNewInputListener mInputListener;
 
+    private boolean inputUp;
+
+    private List<Messager> messagers = new ArrayList<>();
+
     public TerminalManager(TextView terminalView, EditText inputView, TextView prefixView, TextView submitView, SkinManager skinManager,
-                           String hint, final boolean physicalEnter) {
+                           String hint, boolean inputUp) {
         if (terminalView == null || inputView == null || prefixView == null || skinManager == null)
             throw new UnsupportedOperationException();
 
@@ -91,6 +95,8 @@ public class TerminalManager {
             });
         }
 
+        this.inputUp = inputUp;
+
         this.mTerminalView = terminalView;
         this.mTerminalView.setTypeface(mSkinManager.getGlobalTypeface());
         this.mTerminalView.setTextSize(mSkinManager.getTextSize());
@@ -119,11 +125,16 @@ public class TerminalManager {
         });
     }
 
+    public void addMessager(Messager messager) {
+        messagers.add(messager);
+    }
+
     private void setupNewInput() {
         mInputView.setText(Tuils.EMPTYSTRING);
-        originalInput = Tuils.EMPTYSTRING;
-        propertyText = new ArrayList<>();
+
         mCurrentOutputId++;
+        globalId++;
+
         requestInputFocus();
     }
 
@@ -151,6 +162,16 @@ public class TerminalManager {
             return;
 
         writeToView(output, OUTPUT, id);
+
+        int counter = 0;
+        for(Messager messager : messagers) {
+            if(globalId != 0 && globalId % messager.n == 0) {
+                counter++;
+                writeToView(messager.message, OUTPUT, ++mCurrentOutputId);
+            }
+        }
+        globalId += counter;
+
         scrollToEnd();
     }
 
@@ -159,22 +180,27 @@ public class TerminalManager {
     }
 
     private void writeToView(final String text, final int type, int id) {
-        if(type == INPUT || id == mCurrentOutputId) {
-            if(!mTerminalView.getText().toString().endsWith(Tuils.NEWLINE)) {
-                mTerminalView.append(Tuils.NEWLINE);
-            }
-
+//        this is for when an input or a std (synchronous) output happens
+        if(type == INPUT || id >= mCurrentOutputId) {
             if(Looper.myLooper() == Looper.getMainLooper()) {
+                if(!mTerminalView.getText().toString().endsWith(Tuils.NEWLINE)) {
+                    mTerminalView.append(Tuils.NEWLINE);
+                }
                 mTerminalView.append(getSpannable(text, type));
             } else {
                 ((Activity) mTerminalView.getContext()).runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
+                        if(!mTerminalView.getText().toString().endsWith(Tuils.NEWLINE)) {
+                            mTerminalView.append(Tuils.NEWLINE);
+                        }
                         mTerminalView.append(getSpannable(text, type));
                     }
                 });
             }
-        } else if(type == OUTPUT) {
+        }
+//        this is for when a delayed output happens
+        else if(type == OUTPUT) {
             List<String> oldText = getLines(mTerminalView);
             List<Map.Entry<String, String>> wrappedOldText = splitInputOutput(oldText);
 
@@ -329,16 +355,14 @@ public class TerminalManager {
         });
     }
 
-    public static class InputText {
+    public static class Messager {
 
-        String original;
-        CharSequence shownText;
-        Runnable onClick;
+        int n;
+        String message;
 
-        public InputText(String original, CharSequence shownText, Runnable onClick) {
-            this.original = original;
-            this.shownText = shownText;
-            this.onClick = onClick;
+        public Messager(int n, String message) {
+            this.n = n;
+            this.message = message;
         }
     }
 
diff --git a/app/src/main/res/raw/settings.txt b/app/src/main/res/raw/settings.txt
index bc15a21..e3bf142 100755
--- a/app/src/main/res/raw/settings.txt
+++ b/app/src/main/res/raw/settings.txt
@@ -1,5 +1,5 @@
 // do not edit the settingsVersion property!
-settingsVersion=4.7
+settingsVersion=4.8
 
 // when you are ready go back to t-ui and type "restart" to see changes
 
@@ -48,3 +48,4 @@ closeOnDbTap=true
 showSuggestions=true
 enableEnterInPhysicalKeyboard=false
 compareStringForApps=false
+showDonationMessage=true
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3ae93fe..43b7139 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -6,10 +6,11 @@
     <string name="separator_text_su">$</string>
     <string name="su">T-UI got SU permission</string>
     <string name="nosu">Your device isn\'t rooted</string>
-    <string name="permissions_toast">You have to grant at least Storage permission to make t-ui work</string>
-    <string name="permission_error">t-ui hasn\'t the permission to do that</string>
+    <string name="permissions_toast">You have to grant Storage permission at least</string>
     <string name="version_label">Version:</string>
 
+    <string name="rate_donate_text">Do you like my app? Rate T-UI on Play Store (command: >>rate) or offer me a coffee (command: >>donate)</string>
+
     <!-- app mgr -->
     <string name="output_appnotfound">Application or package not found</string>
     <string name="output_hideapp">state: hidden</string>
@@ -27,7 +28,8 @@
 
     <!-- various outputs -->
     <string name="output_nothingfound">No matches</string>
-    <string name="output_nopermissions">You didn\'t grant the permission to do that</string>
+    <string name="output_nopermissions">You didn\'t grant the permission</string>
+    <string name="output_waitingpermission">Waiting for permission</string>
 
     <!-- search-->
     <string name="output_searchingplaystore">Play Store search:</string>
@@ -37,7 +39,7 @@
     <string name="output_search_file">File Search here:</string>
     <string name="output_nothing_found">Nothing was found</string>
 
-    <!-- telephony -->
+    <!-- phone -->
     <string name="output_wifi">WiFi active:</string>
     <string name="output_data">Mobile Data active:</string>
     <string name="output_numbernotfound">Contact not found</string>
@@ -108,7 +110,7 @@
         \napps -h whatsapp
     </string>
     <string name="help_clear">Clear the screen</string>
-    <string name="help_call">Perform a call to someone;
+    <string name="help_call">Perform a call to someone or get contacts;
         \nUsage: call number OR name
         \n\ncall MOTHER
     </string>
-- 
GitLab