From 2326c521997e014cef9d88a88f427bfac1a14791 Mon Sep 17 00:00:00 2001 From: Fredrik Fornwall Date: Wed, 23 Dec 2015 01:43:41 +0100 Subject: [PATCH] Implement support for termux.properties Also some symlink-to-storage improvements and experimenting with requesting read storage permission. --- .../java/com/termux/app/TermuxActivity.java | 82 ++++++++++++++---- .../java/com/termux/app/TermuxInstaller.java | 48 +++++++--- .../com/termux/view/TerminalKeyListener.java | 4 + .../java/com/termux/view/TerminalView.java | 5 +- app/src/main/res/raw/bell.ogg | Bin 0 -> 5090 bytes 5 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 app/src/main/res/raw/bell.ogg diff --git a/app/src/main/java/com/termux/app/TermuxActivity.java b/app/src/main/java/com/termux/app/TermuxActivity.java index dda72b7c..95be34ef 100644 --- a/app/src/main/java/com/termux/app/TermuxActivity.java +++ b/app/src/main/java/com/termux/app/TermuxActivity.java @@ -1,19 +1,8 @@ package com.termux.app; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.termux.R; -import com.termux.drawer.DrawerLayout; -import com.termux.terminal.TerminalSession; -import com.termux.terminal.TerminalSession.SessionChangedCallback; -import com.termux.view.TerminalKeyListener; -import com.termux.view.TerminalView; - +import android.Manifest; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; @@ -27,11 +16,15 @@ import android.content.DialogInterface.OnShowListener; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; +import android.media.AudioAttributes; +import android.media.SoundPool; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Vibrator; @@ -64,6 +57,19 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; +import com.termux.R; +import com.termux.drawer.DrawerLayout; +import com.termux.terminal.TerminalSession; +import com.termux.terminal.TerminalSession.SessionChangedCallback; +import com.termux.view.TerminalKeyListener; +import com.termux.view.TerminalView; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * A terminal emulator activity. * @@ -114,6 +120,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection */ boolean mIsVisible; + private SoundPool mBellSoundPool = new SoundPool.Builder().setMaxStreams(1).setAudioAttributes( + new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION).build()).build(); + private int mBellSoundId; + private final BroadcastReceiver mBroadcastReceiever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -125,6 +136,16 @@ public final class TermuxActivity extends Activity implements ServiceConnection } }; + /** For processes to access shared internal storage (/sdcard) we need this permission. */ + @TargetApi(Build.VERSION_CODES.M) + public void ensureStoragePermissionGranted() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1234); + } + } + } + @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); @@ -229,11 +250,22 @@ public final class TermuxActivity extends Activity implements ServiceConnection @Override public void onSingleTapUp(MotionEvent e) { - // Toggle keyboard visibility if tapping with a finger: - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); + switch (mSettings.mTapBehaviour) { + case TermuxPreferences.TAP_TOGGLE_KEYBOARD: + // Toggle keyboard visibility if tapping with a finger: + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); + break; + case TermuxPreferences.TAP_SHOW_MENU: + mTerminalView.showContextMenu(); + break; + } } + @Override + public boolean shouldBackButtonBeMappedToEscape() { + return mSettings.mBackIsEscape; + } }); findViewById(R.id.new_session_button).setOnClickListener(new OnClickListener() { @@ -294,7 +326,11 @@ public final class TermuxActivity extends Activity implements ServiceConnection mTerminalView.checkForTypeface(); mTerminalView.checkForColors(); - TermuxInstaller.setupStorageSymlink(this); + ensureStoragePermissionGranted(); + + TermuxInstaller.setupStorageSymlinks(this); + + mBellSoundId = mBellSoundPool.load(this, R.raw.bell, 1); } /** @@ -351,7 +387,17 @@ public final class TermuxActivity extends Activity implements ServiceConnection @Override public void onBell(TerminalSession session) { - if (mIsVisible) ((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50); + if (mIsVisible) { + switch (mSettings.mBellBehaviour) { + case TermuxPreferences.BELL_BEEP: + mBellSoundPool.play(mBellSoundId, 1.f, 1.f, 1, 0, 1.f); + break; + case TermuxPreferences.BELL_VIBRATE: + ((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(50); + break; + } + + } } }; diff --git a/app/src/main/java/com/termux/app/TermuxInstaller.java b/app/src/main/java/com/termux/app/TermuxInstaller.java index a98a89e1..9265fed5 100644 --- a/app/src/main/java/com/termux/app/TermuxInstaller.java +++ b/app/src/main/java/com/termux/app/TermuxInstaller.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnDismissListener; +import android.os.Environment; import android.system.Os; import android.util.Log; import android.util.Pair; @@ -200,31 +201,50 @@ final class TermuxInstaller { } } - public static void setupStorageSymlink(final Context context) { - final File[] dirs = context.getExternalFilesDirs(null); - if (dirs == null || dirs.length < 2) return; + public static void setupStorageSymlinks(final Context context) { new Thread() { public void run() { try { - final File externalDir = dirs[1]; File homeDir = new File(TermuxService.HOME_PATH); homeDir.mkdirs(); - File externalLink = new File(homeDir, "storage"); + File storageDir = new File(homeDir, "storage"); - if (externalLink.exists()) { - if (externalLink.getCanonicalPath().equals(externalDir.getPath())) { - // Keeping existing link. + if (storageDir.exists()) { + if (storageDir.isDirectory()) { return; } else { - // Removing old link to give place to new. - if (!externalLink.delete()) { - Log.e("termux", "Unable to remove old $HOME/storage to give place for new"); - return; - } + storageDir.delete(); } } - Os.symlink(externalDir.getAbsolutePath(), externalLink.getAbsolutePath()); + storageDir.mkdirs(); + + File sharedDir = Environment.getExternalStorageDirectory(); + Os.symlink(sharedDir.getAbsolutePath(), new File(storageDir, "shared").getAbsolutePath()); + + File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + Os.symlink(downloadsDir.getAbsolutePath(), new File(storageDir, "downloads").getAbsolutePath()); + + File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); + Os.symlink(dcimDir.getAbsolutePath(), new File(storageDir, "dcim").getAbsolutePath()); + + File documentsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); + Os.symlink(documentsDir.getAbsolutePath(), new File(storageDir, "documents").getAbsolutePath()); + + File picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + Os.symlink(picturesDir.getAbsolutePath(), new File(storageDir, "pictures").getAbsolutePath()); + + File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); + Os.symlink(musicDir.getAbsolutePath(), new File(storageDir, "music").getAbsolutePath()); + + File moviesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES); + Os.symlink(moviesDir.getAbsolutePath(), new File(storageDir, "movies").getAbsolutePath()); + + final File[] dirs = context.getExternalFilesDirs(null); + if (dirs == null || dirs.length >= 2) { + final File externalDir = dirs[1]; + Os.symlink(externalDir.getAbsolutePath(), new File(storageDir, "external").getAbsolutePath()); + } } catch (Exception e) { Log.e("termux", "Error setting up link", e); } diff --git a/app/src/main/java/com/termux/view/TerminalKeyListener.java b/app/src/main/java/com/termux/view/TerminalKeyListener.java index 184d1d8e..83109b86 100644 --- a/app/src/main/java/com/termux/view/TerminalKeyListener.java +++ b/app/src/main/java/com/termux/view/TerminalKeyListener.java @@ -6,6 +6,8 @@ import android.view.ScaleGestureDetector; /** * Input and scale listener which may be set on a {@link TerminalView} through * {@link TerminalView#setOnKeyListener(TerminalKeyListener)}. + * + * TODO: Rename to TerminalViewClient. */ public interface TerminalKeyListener { @@ -17,4 +19,6 @@ public interface TerminalKeyListener { /** On a single tap on the terminal if terminal mouse reporting not enabled. */ void onSingleTapUp(MotionEvent e); + boolean shouldBackButtonBeMappedToEscape(); + } diff --git a/app/src/main/java/com/termux/view/TerminalView.java b/app/src/main/java/com/termux/view/TerminalView.java index d8fdec8a..415e9b68 100644 --- a/app/src/main/java/com/termux/view/TerminalView.java +++ b/app/src/main/java/com/termux/view/TerminalView.java @@ -12,7 +12,6 @@ import com.termux.terminal.TerminalEmulator; import com.termux.terminal.TerminalSession; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Typeface; @@ -492,7 +491,7 @@ public final class TerminalView extends View { @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (LOG_KEY_EVENTS) Log.i(EmulatorDebug.LOG_TAG, "onKeyPreIme(keyCode=" + keyCode + ", event=" + event + ")"); - if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) { + if (keyCode == KeyEvent.KEYCODE_ESCAPE || (keyCode == KeyEvent.KEYCODE_BACK && mOnKeyListener.shouldBackButtonBeMappedToEscape())) { // Handle the escape key ourselves to avoid the system from treating it as back key // and e.g. close keyboard. switch (event.getAction()) { @@ -518,7 +517,7 @@ public final class TerminalView extends View { if (handleVirtualKeys(keyCode, event, true)) { invalidate(); return true; - } else if (event.isSystem() && keyCode != KeyEvent.KEYCODE_BACK) { + } else if (event.isSystem() && (!mOnKeyListener.shouldBackButtonBeMappedToEscape() || keyCode != KeyEvent.KEYCODE_BACK)) { return super.onKeyDown(keyCode, event); } diff --git a/app/src/main/res/raw/bell.ogg b/app/src/main/res/raw/bell.ogg new file mode 100644 index 0000000000000000000000000000000000000000..674f25d02621bf91e077455a824a8d723b0c1ddf GIT binary patch literal 5090 zcmeZIPY-5bVt@kqns00%=B;@Z2N>lT%kqnoGK)b1j4o?H6axc8IU|^{gAuF~%mxu4 zogkT*SCW?nbpNA(lgto>6@!obEWcq$kf7?>EC8Cil1Vq}FV z)^u_XV`Si9U|=xxQ8?OgVL~Vag8&0Vf{G^J<0b(6B=$tX7^Qc5} zkKr*GpW7);Y^`3H83B1(un97Rwdby8<6ravu7smYlb2%d+ge zbDF9gypgO73``6T9*V6ZK_DWjL$OUFsY9_{rs#y?@;OCZS}PVbaT%RZX!bFBxn#1R z)yox&`>bARY`NgXt-IxtmyhAqu;g`n*e*v&K{`i^Ox%l2ju+b;FSc94(YNc~QkbNcVD)X?U^i^sXachK_ToZ@qxw`OPWD930-mTB(6|b}O$X>ejT9o$EDM4PEOTpm>${-3U6CBwj!I`9OM$swF{9yhK7WOm*qxIFTK6JHTFj7{p+>&ulL4_x1lF2 zc+O#P;!tb>=gBEaJ%-2UH2XPmLLwNJa|%U{Pn0=1QN??citib`3P~ z4Hk{PC|Y`1EH&6H_OfZ}<WMt1c%`>71o88YzvP_v@91e=CR~eDw*J@Zo6ZWX$ z@qB8*;p7~~!Vt*7&|rAN@Q6gwInR>|T+SJC&Yg0?Qhn*FRhJ1RKc2Jr zlIO`KQzX2*<}MLR?p({j(7?dX(7?dG@WKR9Mh6iFh9sUKF3r{{LFY7|O$a(=_*5py z%X0ag;B%H7vMr|!+2%G2XfBlrKBswHrsa&|(>cu-95omi6b>*lC>(IoFiCM`5Kw1$ zP@r;tvWd5ds<*J~d6CrX!oJt1nw&K;4GalAEgX6^B=okJZ|F8vUtwS0FCn4AuA!o# zm#6uLZp}S!l6o^F^sZRw)orP_#p=SRmEJe@jT8&Ldd)X(C}-u-Xft_gMDv`hK6nnz4Vm6R)KvZZEyBkvS_fYtf2Tvouz%TDfXl?y7ZbqZhqc zwP@9;P3f!FtvR)6{pxi&uV-njTDM}=CY@EQbXIR#^Lv%f>uoDm?OJs` zjwyund7hXvS;#9xbIPS4_JvCWys{TgxfYbYaoLrmw+c(dyfSsCT#CwCdF)D5k6^NZ z(eouK=R7%=ZUIRY`*}TIu!PUaYv~dZZ!g`jGoB|GEWYIEv2uw}u;5M6SPF^#cCwY0TojNJ# z%$&&}qni7?JTo9Y|~}evOp5ixwhA4 z6??6{b|t7+_t>oD+`U(4RlkS%JSj`}*tMj!Aj`EjPMs8$WBBY=RFCD9OG%z9k4_5e z(LFXRI%nsyX<0dj&t?U6Z+&*FILGwcm8dl!JG{4Eiz@El`8AAz;Xng3gF^#n>H<&^ z0?M|I9YN)rSsTq34kwbOsIZI8=qt`4sRF{f*Yv~>h%huG~ zDweIwz_0+EZP|P~k1}%cMshN^Ffu3{Q#c~g!X~*?qWOZv5|Ly9rKJ+ZeqKvOlFxab zlqlwN(vnO*=cu9C65yq!*(%`Wsk)Sjfq_egfq^OK!juS^1+a1?fhUQ_uuY*P#7Il9 z#m7s1Wurh8_uA5HLD@@>o{7p@yXA%h$J*9wM&7E&u0*A+Ju)Lon}OlY0Y-*5;BrKj zVFEjYgT!Ky0&0`8vCIu}PNfz+(Tv^29q^Wr{3?wln>W#vbOHnzdWddHAy2qlNz1AL$$X=(o z^jgvy)9Y8FdbU2BmF=Z_EGql#P6mcMtn3UMn5A9K)Fn8zOj5v}Xi!)plH6n1CX&=; z_Q<8c>?T;Qqa2xj643L-%h?rB{2a-T)+=98BSUjw7o-Ha;SQCBwc|0l(b;RaUW-B|7##Le+fMD%*4A)@w=Krq`ml z86M;nFf@2hm55RG)$jHFr1P*Ob#~`T7@c9BzYEg9YGJGZjN+XIbJutDB z32-q1tLKoU)e_|O9HMSYfD<@raV%^J^3s%T5pdD~vs7CIoH!v3IB;TY5%SVpIt82} zL5;d8LQWit;O5PF)%bRgGyoL7b=TS zDdm_vJj5K3al(tCfro*i@J7~a&1)wVj?QVB=G`)(NFYg1FsRGYePgPJBu;@bva>>E?c6 zbbvMXPN_nR0*hkDQAUvo4b8%N6_0rRcO2xja$U*?>GCo%$T2WX;#E*mnq}F?*Uvve zV4~n8p~=Eiz&)S`j1Bw@4Jxc19UWTXV+_DZYg>8AI^3cNj!t=<~k z`o+PdBldTPp2@nujGl9*PTF{+E@uUEZO>BNo!^uw6j=$SBmdy)b)xThwoKn%(FgOT0Q;B(+4H> zN`Gh82L{&b=^wwy*>(Jt(rkt(OSLunjanLK;ty}Gn|t3(>2&hE=hJt#oAKEF+rf52 zm?7p1Kg07E{0+<7Sl=}NWAt*cH(WTsJ^!>ex$4pUwJGt6Tx4mc>I zcBU|)k0p|YVFBZs(pyho-I`>!v2?SXgpTd=V5y5$9ZHSuZ#ow$HNKK%t*w99rNPjV z+QIkvj?#;V3=9g5pMI)+VPM#G`tm$^Murzv+cZTP7$o%7Ss4~2q(5h3UttY>+%b=_1luNnV!@t7&kt@$FSzWQ!Wv1gTpo%IU5ek zusmZ}bDSaJtj*ek9#^DJ+X){$y7$4qHE(Wyzx=%R&fDI+Z*jgZcTBZT96ZxGO-XOZ z%q{c6R@Lt1kZ@jqm`P*71I06zH(M207JT2zQ!d-Ua4`F?$@OY>hJrd4DXs}!w;~T} zC0Cvc`f*v}!<@u-(aZJ)Z7}lI_j%30z|i2KRTcckCMBwU;`s%RQ}=fC3Tc9_@8<%a1GmnWDlHj*1V1gP0ov-l<-Cclo~!x5S^r@7OkEtunRe^(~mQ g)c3+C1|K66(Z(xBI?}@>xyphV85llsb2Bgi0M0Tp(f|Me literal 0 HcmV?d00001