From 0ff1f98198c72320e24fb674e501da62a57150bb Mon Sep 17 00:00:00 2001
From: Francesco Andreuzzi <andreuzzi.francesco@gmail.com>
Date: Sun, 1 Jan 2017 22:44:40 +0100
Subject: [PATCH] 4.11

---
 app/build.gradle                              |   6 +-
 app/src/main/AndroidManifest.xml              |  22 ++-
 .../consolelauncher/LauncherActivity.java     |  22 ++-
 .../ohi/andre/consolelauncher/UIManager.java  |  21 ++-
 .../commands/raw/airplane.java                |   2 +-
 .../consolelauncher/commands/raw/data.java    | 132 ++++++++++++++++--
 .../consolelauncher/commands/raw/share.java   |   9 +-
 .../consolelauncher/commands/raw/status.java  |  17 ++-
 .../consolelauncher/managers/AppsManager.java |  78 ++++++++---
 .../consolelauncher/managers/FileManager.java |   2 +-
 .../managers/PreferencesManager.java          |   5 +
 .../consolelauncher/managers/SkinManager.java |  91 ++++++------
 .../managers/SuggestionsManager.java          |  19 +--
 .../andre/consolelauncher/tuils/Tuils.java    |  88 +++++++++++-
 app/src/main/res/raw/settings.txt             |   3 +-
 app/src/main/res/values/strings.xml           |   6 +-
 app/src/main/res/xml/provider_paths.xml       |   6 +
 17 files changed, 403 insertions(+), 126 deletions(-)
 create mode 100644 app/src/main/res/xml/provider_paths.xml

diff --git a/app/build.gradle b/app/build.gradle
index b9402d1..76d627b 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,9 +6,9 @@ android {
     defaultConfig {
         applicationId "ohi.andre.consolelauncher"
         minSdkVersion 8
-        targetSdkVersion 23
-        versionCode 78
-        versionName "4.8"
+        targetSdkVersion 25
+        versionCode 81
+        versionName "4.10"
     }
 
     buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7d098ee..1a00f15 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,12 @@
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
+    <permission android:name="android.permission.FLASHLIGHT"
+        android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
+        android:protectionLevel="normal"
+        android:label="@string/help_flash"
+        android:description="@string/help_flash" />
+
     <!-- features -->
     <uses-feature
         android:name="android.hardware.camera"
@@ -50,16 +56,13 @@
         <activity
             android:name=".LauncherActivity"
             android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize"
-            android:launchMode="singleTask"
+            android:launchMode="singleInstance"
             android:stateNotNeeded="true"
-            android:excludeFromRecents="true"
             android:windowSoftInputMode="stateAlwaysVisible|adjustResize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-
                 <category android:name="android.intent.category.HOME" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
 
@@ -99,6 +102,17 @@
                 android:value=".tuils.tutorial.TutorialIndexActivity" />
         </activity>
 
+
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="${applicationId}.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths"/>
+        </provider>
+
     </application>
 
 </manifest>
\ 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 a1ebd2c..a65f3ff 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
@@ -1,6 +1,7 @@
 package ohi.andre.consolelauncher;
 
 import android.Manifest;
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -49,6 +50,8 @@ public class LauncherActivity extends Activity implements Reloadable {
 
     private PreferencesManager preferencesManager;
 
+    private Intent starterIntent;
+
     private CommandExecuter ex = new CommandExecuter() {
 
         @Override
@@ -126,6 +129,7 @@ public class LauncherActivity extends Activity implements Reloadable {
     private void finishOnCreate() {
         File tuiFolder = getFolder();
         Resources res = getResources();
+        starterIntent = getIntent();
 
         DevicePolicyManager policy = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
         ComponentName component = new ComponentName(this, PolicyReceiver.class);
@@ -245,9 +249,17 @@ public class LauncherActivity extends Activity implements Reloadable {
 
     @Override
     public void reload() {
-        Intent intent = getIntent();
-        startActivity(intent);
-        finish();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+            reloadOver11();
+        } else {
+            finish();
+            startActivity(starterIntent);
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+    private void reloadOver11() {
+        recreate();
     }
 
     @Override
@@ -256,7 +268,9 @@ public class LauncherActivity extends Activity implements Reloadable {
 
         if (hasFocus) {
             hideStatusBar();
-            ui.focusTerminal();
+            if (ui != null) {
+                ui.focusTerminal();
+            }
         }
     }
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
index 877cf04..d030c2d 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
@@ -5,6 +5,7 @@ import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.Typeface;
 import android.os.Build;
 import android.os.Handler;
@@ -98,6 +99,8 @@ public class UIManager implements OnTouchListener {
         }
     };
 
+    boolean doubleTapSU = false;
+
     protected TextWatcher textWatcher = new TextWatcher() {
 
         @Override
@@ -417,8 +420,10 @@ public class UIManager implements OnTouchListener {
             policy = null;
             component = null;
             det = null;
-        } else
+        } else {
+            doubleTapSU = Boolean.parseBoolean(prefsMgr.getValue(PreferencesManager.DOUBLETAP_SU));
             initDetector();
+        }
 
         mTerminalAdapter = new TerminalManager(terminalView, inputView, prefixView, submitView, skinManager, getHint(prefsMgr), inputBottom);
         mTerminalAdapter.setInputListener(new OnNewInputListener() {
@@ -530,6 +535,8 @@ public class UIManager implements OnTouchListener {
 
         det.setOnDoubleTapListener(new OnDoubleTapListener() {
 
+            boolean hadSU = false;
+
             @Override
             public boolean onSingleTapConfirmed(MotionEvent e) {
                 return false;
@@ -542,9 +549,13 @@ public class UIManager implements OnTouchListener {
 
             @Override
             public boolean onDoubleTap(MotionEvent e) {
-                if(Tuils.verifyRoot()) {
+                if(doubleTapSU) {
+                    hadSU = Tuils.verifyRoot();
+                    doubleTapSU = hadSU;
+                }
+
+                if(hadSU) {
                     ShellUtils.execCommand("input keyevent 26", true, null);
-                    return true;
                 } else {
                     boolean admin = policy.isAdminActive(component);
 
@@ -552,9 +563,9 @@ public class UIManager implements OnTouchListener {
                         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/raw/airplane.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/airplane.java
index 5a124f3..1bf6343 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/airplane.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/airplane.java
@@ -27,7 +27,7 @@ public class airplane implements CommandAbstraction {
 
             return info.res.getString(R.string.output_airplane) + !isEnabled;
         } else
-            return info.res.getString(R.string.output_noairplane);
+            return info.res.getString(R.string.output_nofeature);
     }
 
     private boolean isEnabled(Context context) {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/data.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/data.java
index efd1f99..c458151 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/data.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/data.java
@@ -1,16 +1,24 @@
 package ohi.andre.consolelauncher.commands.raw;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.State;
 import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.widget.Toast;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecInfo;
+import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class data implements CommandAbstraction {
 
@@ -22,34 +30,128 @@ public class data implements CommandAbstraction {
     }
 
     private boolean toggle(ExecInfo info) {
-        if (info.connectivityMgr == null) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            if (info.connectivityMgr == null) {
+                try {
+                    init(info);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+
+            boolean mobileConnected;
+
+            if (info.wifi == null)
+                info.wifi = (WifiManager) info.context.getSystemService(Context.WIFI_SERVICE);
+
+            if (info.wifi.isWifiEnabled())
+                mobileConnected = true;
+            else {
+                NetworkInfo mobileInfo = info.connectivityMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+                State state = mobileInfo.getState();
+                mobileConnected = state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING;
+            }
+
             try {
-                init(info);
+                info.setMobileDataEnabledMethod.invoke(info.connectMgr, !mobileConnected);
             } catch (Exception e) {
                 e.printStackTrace();
             }
-        }
 
-        boolean mobileConnected;
+            return !mobileConnected;
+        } else {
+            if (!Tuils.verifyRoot()) {
+                Toast.makeText(info.context, R.string.output_nofeature, Toast.LENGTH_SHORT).show();
+                return false;
+            }
 
-        if (info.wifi == null)
-            info.wifi = (WifiManager) info.context.getSystemService(Context.WIFI_SERVICE);
+            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
+                try {
+                    return toggleDataLollipop(info.context);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            } else {
+                try {
+                    return toggleDataAboveLollipop(info.context);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return false;
+    }
 
-        if (info.wifi.isWifiEnabled())
-            mobileConnected = true;
-        else {
-            NetworkInfo mobileInfo = info.connectivityMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-            State state = mobileInfo.getState();
-            mobileConnected = state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING;
+    private boolean toggleDataLollipop(Context context) throws Exception {
+        String command = null;
+        int state = isMobileDataEnabledFromLollipop(context) ? 0 : 1;
+        String transactionCode = getTransactionCode(context);
+        if (transactionCode != null && transactionCode.length() > 0) {
+            command = "service call phone " + transactionCode + " i32 " + state;
+            return executeCommandViaSu("-c", command);
         }
+        return false;
+    }
 
+    private static String getTransactionCode(Context context) throws Exception {
         try {
-            info.setMobileDataEnabledMethod.invoke(info.connectMgr, !mobileConnected);
+            final TelephonyManager mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+            final Class<?> mTelephonyClass = Class.forName(mTelephonyManager.getClass().getName());
+            final Method mTelephonyMethod = mTelephonyClass.getDeclaredMethod("getITelephony");
+            mTelephonyMethod.setAccessible(true);
+            final Object mTelephonyStub = mTelephonyMethod.invoke(mTelephonyManager);
+            final Class<?> mTelephonyStubClass = Class.forName(mTelephonyStub.getClass().getName());
+            final Class<?> mClass = mTelephonyStubClass.getDeclaringClass();
+            final Field field = mClass.getDeclaredField("TRANSACTION_setDataEnabled");
+            field.setAccessible(true);
+            return String.valueOf(field.getInt(null));
         } catch (Exception e) {
-            e.printStackTrace();
+            throw e;
+        }
+    }
+
+    private static boolean isMobileDataEnabledFromLollipop(Context context) {
+        boolean state = false;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            state = Settings.Global.getInt(context.getContentResolver(), "mobile_data", 0) == 1;
         }
+        return state;
+    }
 
-        return !mobileConnected;
+    private static boolean executeCommandViaSu(String option, String command) {
+        String su = "su";
+        for (int i=0; i < 3; i++) {
+            if (i == 1) {
+                su = "/system/xbin/su";
+            } else if (i == 2) {
+                su = "/system/bin/su";
+            }
+            try {
+                Runtime.getRuntime().exec(new String[]{su, option, command});
+            } catch (Exception e) {
+                return false;
+            } finally {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
+    private boolean toggleDataAboveLollipop(Context context) throws Exception {
+        SubscriptionManager mSubscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        String command = null;
+        int state = isMobileDataEnabledFromLollipop(context) ? 0 : 1;
+        String transactionCode = getTransactionCode(context);
+        for (int i = 0; i < mSubscriptionManager.getActiveSubscriptionInfoCountMax(); i++) {
+            if (transactionCode != null && transactionCode.length() > 0) {
+                int subscriptionId = mSubscriptionManager.getActiveSubscriptionInfoList().get(i).getSubscriptionId();
+                command = "service call phone " + transactionCode + " i32 " + subscriptionId + " i32 " + state;
+                return executeCommandViaSu("-c", command);
+            }
+        }
+        return false;
     }
 
     private void init(ExecInfo info) throws Exception {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/share.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/share.java
index 8bde354..80fc6b5 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/share.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/share.java
@@ -8,6 +8,7 @@ import java.io.File;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecInfo;
+import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class share implements CommandAbstraction {
 
@@ -17,12 +18,8 @@ public class share implements CommandAbstraction {
         if (f.isDirectory())
             return info.res.getString(R.string.output_isdirectory);
 
-        Intent sharingIntent = new Intent(Intent.ACTION_SEND);
-        Uri uri = Uri.fromFile(f);
-        sharingIntent.setType("*/*");
-        sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
-        info.context.startActivity(Intent.createChooser(sharingIntent,
-                info.res.getString(R.string.share_label)));
+        Intent sharingIntent = Tuils.shareFile(info.context, f);
+        info.context.startActivity(Intent.createChooser(sharingIntent, info.res.getString(R.string.share_label)));
 
         return info.res.getString(R.string.sharing) + " " + f.getName();
     }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/status.java b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/status.java
index edc7f5f..f0822c3 100644
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/raw/status.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/raw/status.java
@@ -5,6 +5,8 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.os.Build;
+import android.provider.Settings;
 
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
@@ -33,13 +35,20 @@ public class status implements CommandAbstraction {
         }
         level *= 100;
 
-        ConnectivityManager cm = (ConnectivityManager) info.context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
-        boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;
+        boolean connected = false;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                connected = Settings.Global.getInt(info.context.getContentResolver(), "mobile_data", 0) == 1;
+            }
+        } else {
+            ConnectivityManager cm = (ConnectivityManager) info.context.getSystemService(Context.CONNECTIVITY_SERVICE);
+            NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+            boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;
+        }
 
         return info.res.getString(R.string.battery_charge) + Tuils.SPACE + (int) level + PERCENTAGE + Tuils.NEWLINE +
                 info.res.getString(R.string.wifi) + Tuils.SPACE + wifiConnected + Tuils.NEWLINE +
-                info.res.getString(R.string.mobile_data) + Tuils.SPACE + isMobile;
+                info.res.getString(R.string.mobile_data) + Tuils.SPACE + connected;
     }
 
     @Override
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
index 7f46d96..d9a7891 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
@@ -11,6 +11,7 @@ import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -443,9 +444,22 @@ public class AppsManager {
     private static class AppUtils {
 
         public static void checkEquality(List<AppInfo> list) {
+
+            First:
             for (AppInfo info : list) {
+
+                Second:
                 for (int count = 0; count < list.size(); count++) {
                     AppInfo info2 = list.get(count);
+
+                    if(info == null || info.publicLabel == null) {
+                        continue First;
+                    }
+
+                    if(info2 == null || info2.publicLabel == null) {
+                        continue Second;
+                    }
+
                     if (!info.equals(info2) && info.publicLabel.equals(info2.publicLabel)) {
                         list.set(count, new AppInfo(info2.packageName, getNewLabel(info2.publicLabel, info2.packageName),
                                 info2.launchedTimes));
@@ -456,27 +470,55 @@ public class AppsManager {
 
         public static String getNewLabel(String oldLabel, String packageName) {
             try {
-                int firstDot = packageName.indexOf(Tuils.DOT) + 1;
-                int secondDot = packageName.substring(firstDot).indexOf(Tuils.DOT) + firstDot;
-
-                StringBuilder newLabel = new StringBuilder();
-                if (firstDot == -1) {
-                    newLabel.append(packageName);
-                    newLabel.append(Tuils.SPACE);
-                    newLabel.append(oldLabel);
-                } else if (secondDot == -1) {
-                    newLabel.append(packageName.substring(firstDot, packageName.length()));
-                    newLabel.append(Tuils.SPACE);
-                    newLabel.append(oldLabel);
+
+//                              OLD VERSION OF this method
+//
+//                int firstDot = packageName.indexOf(Tuils.DOT) + 1;
+//                int secondDot = packageName.substring(firstDot).indexOf(Tuils.DOT) + firstDot;
+//
+//                StringBuilder newLabel = new StringBuilder();
+//                if (firstDot == -1) {
+//                    newLabel.append(packageName);
+//                    newLabel.append(Tuils.SPACE);
+//                    newLabel.append(oldLabel);
+//                } else if (secondDot == -1) {
+//                    newLabel.append(packageName.substring(firstDot, packageName.length()));
+//                    newLabel.append(Tuils.SPACE);
+//                    newLabel.append(oldLabel);
+//                } else {
+//                    newLabel.append(packageName.substring(firstDot, secondDot));
+//                    newLabel.append(Tuils.SPACE);
+//                    newLabel.append(oldLabel);
+//                }
+//
+//                String label = newLabel.toString();
+//                return label.substring(0, 1).toUpperCase() + label.substring(1);
+
+                int firstDot = packageName.indexOf(Tuils.DOT);
+                if(firstDot == -1) {
+//                    no dots in package name (nearly impossible)
+                    return packageName;
+                }
+                firstDot++;
+
+                int secondDot = packageName.substring(firstDot).indexOf(Tuils.DOT);
+                String prefix;
+                if(secondDot == -1) {
+//                    only one dot, so two words. The first is most likely to be the company name
+//                    facebook.messenger
+//                    is better than
+//                    messenger.facebook
+                    prefix = packageName.substring(0, firstDot - 1);
+                    prefix = prefix.substring(0,1).toUpperCase() + prefix.substring(1).toLowerCase();
+                    return prefix + Tuils.SPACE + oldLabel;
                 } else {
-                    newLabel.append(packageName.substring(firstDot, secondDot));
-                    newLabel.append(Tuils.SPACE);
-                    newLabel.append(oldLabel);
+//                    two dots or more, the second word is the company name
+                    prefix = packageName.substring(firstDot, secondDot + firstDot);
+                    prefix = prefix.substring(0,1).toUpperCase() + prefix.substring(1).toLowerCase();
+                    return prefix + Tuils.SPACE + oldLabel;
                 }
 
-                String label = newLabel.toString();
-                return label.substring(0, 1).toUpperCase() + label.substring(1);
-            } catch (IndexOutOfBoundsException e) {
+            } catch (Exception e) {
                 return packageName;
             }
         }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
index dae3556..56a544c 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
@@ -192,7 +192,7 @@ public class FileManager {
         if (file.isDirectory())
             return FileManager.ISDIRECTORY;
 
-        Intent intent = Tuils.openFile(file);
+        Intent intent = Tuils.openFile(c, file);
 
         c.startActivity(intent);
         return 0;
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 5575e48..fc433cb 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/PreferencesManager.java
@@ -13,6 +13,7 @@ import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
 
+import ohi.andre.consolelauncher.tuils.ShellUtils;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class PreferencesManager {
@@ -47,6 +48,7 @@ public class PreferencesManager {
     public static final String FILE_SUGGESTION_BG = "fileSuggestionBg";
 
     public static final String DOUBLETAP = "closeOnDbTap";
+    public static final String DOUBLETAP_SU = "doubleTapSU";
     public static final String SHOWSUGGESTIONS = "showSuggestions";
     public static final String EXECUTE_ON_SUGGESTION_CLICK = "executeOnSuggestionClick";
 
@@ -165,6 +167,7 @@ public class PreferencesManager {
             } else { // settings.txt doesn't exist
                 file.createNewFile();
             }
+            ShellUtils.execCommand("chmod 666 " + file.getAbsolutePath(), false, null);
         } else
             return false;
 
@@ -181,6 +184,8 @@ public class PreferencesManager {
             stream.flush();
             stream.close();
 
+            ShellUtils.execCommand("chmod 666 " + file.getAbsolutePath(), false, null);
+
             return true;
         } catch (Exception e) {
             return false;
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/SkinManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/SkinManager.java
index 15b5aa5..ffeaf83 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/SkinManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/SkinManager.java
@@ -4,6 +4,7 @@ import android.graphics.Color;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import java.util.HashMap;
 
@@ -48,15 +49,20 @@ public class SkinManager {
     private boolean useSystemWp;
     private boolean showSuggestions;
 
-    private HashMap<Integer, Integer> suggestionBgs = new HashMap<>();
-
     private int suggestionTextColor;
 
+    private int defaulSuggestionColor;
+    private int appSuggestionColor;
+    private int aliasSuggestionColor;
+    private int musicSuggestionColor;
+    private int contactsSuggestionColor;
+    private int commandSuggestionColor;
+    private int fileSuggestionColor;
+
     private boolean multicolorSuggestions;
     private boolean transparentSuggestions;
 
     public SkinManager(PreferencesManager prefs, Typeface lucidaConsole) {
-
         boolean systemFont = Boolean.parseBoolean(prefs.getValue(PreferencesManager.USE_SYSTEMFONT));
         globalTypeface = systemFont ? Typeface.DEFAULT : lucidaConsole;
 
@@ -125,71 +131,51 @@ public class SkinManager {
                 transparentSuggestions = false;
             }
 
-            if(transparentSuggestions) {
-                suggestionBgs.put(0, Color.TRANSPARENT);
-
-                if(suggestionTextColor == bgColor) {
-                    suggestionTextColor = Color.GREEN;
-                }
+            if(transparentSuggestions && suggestionTextColor == bgColor) {
+                suggestionTextColor = Color.GREEN;
             } else {
-                int defaultSuggestionBg;
                 try {
-                    defaultSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.DEFAULT_SUGGESTION_BG));
+                    defaulSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.DEFAULT_SUGGESTION_BG));
                 } catch (Exception e) {
-                    defaultSuggestionBg = defaultSuggestionBgDefault;
+                    defaulSuggestionColor = defaultSuggestionBgDefault;
                 }
-                suggestionBgs.put(0, defaultSuggestionBg);
-
 
                 if(multicolorSuggestions) {
-
-                    int appSuggestionBg;
                     try {
-                        appSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.APP_SUGGESTION_BG));
+                        appSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.APP_SUGGESTION_BG));
                     } catch (Exception e) {
-                        appSuggestionBg = appSuggestionBgDefault;
+                        appSuggestionColor = appSuggestionBgDefault;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_APP, appSuggestionBg);
 
-                    int contactSuggestionBg;
                     try {
-                        contactSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.CONTACT_SUGGESTION_BG));
+                        contactsSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.CONTACT_SUGGESTION_BG));
                     } catch (Exception e) {
-                        contactSuggestionBg = contactSuggestionBgDefault;
+                        contactsSuggestionColor = contactSuggestionBgDefault;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_CONTACT, contactSuggestionBg);
 
-                    int commandSuggestionsBg;
                     try {
-                        commandSuggestionsBg = Color.parseColor(prefs.getValue(PreferencesManager.COMMAND_SUGGESTION_BG));
+                        commandSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.COMMAND_SUGGESTION_BG));
                     } catch (Exception e) {
-                        commandSuggestionsBg = commandSuggestionsBgDefault;
+                        commandSuggestionColor = commandSuggestionsBgDefault;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_COMMAND, commandSuggestionsBg);
 
-                    int songSuggestionBg;
                     try {
-                        songSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.SONG_SUGGESTION_BG));
+                        musicSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.SONG_SUGGESTION_BG));
                     } catch (Exception e) {
-                        songSuggestionBg = songSuggestionBgDefault;
+                        musicSuggestionColor = songSuggestionBgDefault;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_SONG, songSuggestionBg);
 
-                    int fileSuggestionBg;
                     try {
-                        fileSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.FILE_SUGGESTION_BG));
+                        fileSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.FILE_SUGGESTION_BG));
                     } catch (Exception e) {
-                        fileSuggestionBg = fileSuggestionBgDeafult;
+                        fileSuggestionColor = fileSuggestionBgDeafult;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_FILE, fileSuggestionBg);
 
-                    int aliasSuggestionBg;
                     try {
-                        aliasSuggestionBg = Color.parseColor(prefs.getValue(PreferencesManager.ALIAS_SIGGESTION_BG));
+                        aliasSuggestionColor = Color.parseColor(prefs.getValue(PreferencesManager.ALIAS_SIGGESTION_BG));
                     } catch (Exception e) {
-                        aliasSuggestionBg = aliasSuggestionBgDefault;
+                        aliasSuggestionColor = aliasSuggestionBgDefault;
                     }
-                    suggestionBgs.put(SuggestionsManager.Suggestion.TYPE_ALIAS, aliasSuggestionBg);
                 }
             }
         }
@@ -199,10 +185,6 @@ public class SkinManager {
         return globalTypeface;
     }
 
-    public int getGlobalFontSize() {
-        return globalFontSize;
-    }
-
     public int getDeviceColor() {
         return deviceColor;
     }
@@ -236,7 +218,28 @@ public class SkinManager {
             type = 0;
         }
 
-        return new ColorDrawable(suggestionBgs.get(type));
+        if(transparentSuggestions) {
+            return new ColorDrawable(Color.TRANSPARENT);
+        } else if(!multicolorSuggestions) {
+            return new ColorDrawable(defaulSuggestionColor);
+        } else {
+            switch (type) {
+                case SuggestionsManager.Suggestion.TYPE_APP:
+                    return new ColorDrawable(appSuggestionColor);
+                case SuggestionsManager.Suggestion.TYPE_ALIAS:
+                    return new ColorDrawable(aliasSuggestionColor);
+                case SuggestionsManager.Suggestion.TYPE_COMMAND:
+                    return new ColorDrawable(commandSuggestionColor);
+                case SuggestionsManager.Suggestion.TYPE_CONTACT:
+                    return new ColorDrawable(contactsSuggestionColor);
+                case SuggestionsManager.Suggestion.TYPE_FILE:
+                    return new ColorDrawable(fileSuggestionColor);
+                case SuggestionsManager.Suggestion.TYPE_SONG:
+                    return new ColorDrawable(musicSuggestionColor);
+                default:
+                    return new ColorDrawable(defaulSuggestionColor);
+            }
+        }
     }
 
     public int getSuggestionTextColor() {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/SuggestionsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/SuggestionsManager.java
index 43fe669..b67c948 100644
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/SuggestionsManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/SuggestionsManager.java
@@ -46,16 +46,19 @@ public class SuggestionsManager {
 //            lastword = 0 && before = 0
             if (before.length() == 0) {
                 String[] apps = info.appsManager.getSuggestedApps();
-                for(int count = 0; count < apps.length; count++) {
-                    if(apps[count] == null) {
-                        continue;
+                if (apps != null) {
+                    for(int count = 0; count < apps.length; count++) {
+                        if(apps[count] == null) {
+                            continue;
+                        }
+
+                        float shift = count + 1;
+                        float rate = 1f / shift;
+                        suggestionList.add(new Suggestion(apps[count], true, (int) Math.ceil(rate), Suggestion.TYPE_APP));
                     }
 
-                    float shift = count + 1;
-                    float rate = 1f / shift;
-                    suggestionList.add(new Suggestion(apps[count], true, (int) Math.ceil(rate), Suggestion.TYPE_APP));
+                    return suggestionList.toArray(new Suggestion[suggestionList.size()]);
                 }
-                return suggestionList.toArray(new Suggestion[suggestionList.size()]);
             }
 //            lastword = 0 && before > 0
             else {
@@ -123,7 +126,7 @@ public class SuggestionsManager {
         }
 
         int[] args = cmd.argType();
-        boolean exec = args[args.length - 1] == CommandAbstraction.PARAM;
+        boolean exec = args == null || (args[args.length - 1] == CommandAbstraction.PARAM);
         for (String s : cmd.parameters()) {
             suggestions.add(new Suggestion(s, exec, NO_RATE, 0));
         }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
index c376ca3..c3923ed 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
@@ -17,6 +17,7 @@ import android.os.Build;
 import android.os.Environment;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.support.v4.content.FileProvider;
 import android.text.TextUtils;
 import android.util.Patterns;
 import android.widget.Toast;
@@ -32,6 +33,7 @@ import java.util.List;
 import java.util.regex.Pattern;
 
 import dalvik.system.DexFile;
+import ohi.andre.consolelauncher.BuildConfig;
 import ohi.andre.consolelauncher.managers.MusicManager;
 import ohi.andre.consolelauncher.tuils.tutorial.TutorialIndexActivity;
 
@@ -103,6 +105,7 @@ public class Tuils {
         Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
         intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, component);
         intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, label);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         a.startActivityForResult(intent, 0);
     }
 
@@ -320,18 +323,85 @@ public class Tuils {
     }
 
     public static String getUsername(Context context) {
-        Pattern email = Patterns.EMAIL_ADDRESS;
-        Account[] accs = AccountManager.get(context).getAccounts();
-        for (Account a : accs)
-            if (email.matcher(a.name).matches())
-                return a.name;
+        try {
+            Pattern email = Patterns.EMAIL_ADDRESS;
+            Account[] accs = AccountManager.get(context).getAccounts();
+            for (Account a : accs)
+                if (email.matcher(a.name).matches())
+                    return a.name;
+        } catch (Exception e) {
+            return null;
+        }
         return null;
     }
 
-    public static Intent openFile(File url) {
-        Uri uri = Uri.fromFile(url);
+    public static Intent openFile(Context context, File url) {
+        Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", url);
+
+        Intent intent = new Intent(Intent.ACTION_VIEW);
+        if (url.toString().contains(".doc") || url.toString().contains(".docx")) {
+            // Word document
+            intent.setDataAndType(uri, "application/msword");
+        } else if (url.toString().contains(".apk")) {
+            // apk
+            intent.setDataAndType(uri,
+                    "application/vnd.android.package-archive");
+        } else if (url.toString().contains(".pdf")) {
+            // PDF file
+            intent.setDataAndType(uri, "application/pdf");
+        } else if (url.toString().contains(".ppt")
+                || url.toString().contains(".pptx")) {
+            // Powerpoint file
+            intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
+        } else if (url.toString().contains(".xls")
+                || url.toString().contains(".xlsx")) {
+            // Excel file
+            intent.setDataAndType(uri, "application/vnd.ms-excel");
+        } else if (url.toString().contains(".zip")
+                || url.toString().contains(".rar")) {
+            // ZIP Files
+            intent.setDataAndType(uri, "application/zip");
+        } else if (url.toString().contains(".rtf")) {
+            // RTF file
+            intent.setDataAndType(uri, "application/rtf");
+        } else if (url.toString().contains(".wav")
+                || url.toString().contains(".mp3")) {
+            // WAV audio file
+            intent.setDataAndType(uri, "audio/x-wav");
+        } else if (url.toString().contains(".gif")) {
+            // GIF file
+            intent.setDataAndType(uri, "image/gif");
+        } else if (url.toString().contains(".jpg")
+                || url.toString().contains(".jpeg")
+                || url.toString().contains(".png")) {
+            // JPG file
+            intent.setDataAndType(uri, "image/jpeg");
+        } else if (url.toString().contains(".txt")) {
+            // Text file
+            intent.setDataAndType(uri, "text/plain");
+        } else if (url.toString().contains(".3gp")
+                || url.toString().contains(".mpg")
+                || url.toString().contains(".mpeg")
+                || url.toString().contains(".mpe")
+                || url.toString().contains(".mp4")
+                || url.toString().contains(".avi")) {
+            // Video files
+            intent.setDataAndType(uri, "video/*");
+        } else {
+            intent.setDataAndType(uri, "*/*");
+        }
+
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+
+    public static Intent shareFile(Context c, File url) {
+        Uri uri = FileProvider.getUriForFile(c, BuildConfig.APPLICATION_ID + ".provider", url);
 
         Intent intent = new Intent(Intent.ACTION_VIEW);
+
         if (url.toString().contains(".doc") || url.toString().contains(".docx")) {
             // Word document
             intent.setDataAndType(uri, "application/msword");
@@ -384,8 +454,12 @@ public class Tuils {
             intent.setDataAndType(uri, "*/*");
         }
 
+        intent.putExtra(Intent.EXTRA_STREAM, uri);
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         return intent;
+
     }
 
     public static String getInternalDirectoryPath() {
diff --git a/app/src/main/res/raw/settings.txt b/app/src/main/res/raw/settings.txt
index e3bf142..e27a0c4 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.8
+settingsVersion=4.11
 
 // when you are ready go back to t-ui and type "restart" to see changes
 
@@ -45,6 +45,7 @@ songsFolder=/sdcard/Music
 
 // others
 closeOnDbTap=true
+doubleTapSU=true
 showSuggestions=true
 enableEnterInPhysicalKeyboard=false
 compareStringForApps=false
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 43b7139..d600292 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -43,7 +43,7 @@
     <string name="output_wifi">WiFi active:</string>
     <string name="output_data">Mobile Data active:</string>
     <string name="output_numbernotfound">Contact not found</string>
-    <string name="output_noairplane">Can\'t toggle Airplane Mode on 4.2+ Android Version</string>
+    <string name="output_nofeature">Can\'t use this feature on your Android version</string>
     <string name="output_airplane">Airplane Mode active:</string>
 
     <!-- files -->
@@ -129,10 +129,6 @@
         \n\nExample:
         \nhelp search
     </string>
-    <string name="help_kill">Kill an application
-        \nUsage: kill [appName]
-        \n\nExample:
-        \nkill Settings</string>
     <string name="help_mv">Move one or more files to a folder
         \nUsage: [su] mv [files] [folder]
         \n\nsu mv fool.txt fool2.png myFolder
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000..fafa14f
--- /dev/null
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <external-path
+        name="external_files"
+        path="." />
+</paths>
\ No newline at end of file
-- 
GitLab