diff options
author | Steve Slaven <bpk@hoopajoo.net> | 2011-01-04 23:11:37 (GMT) |
---|---|---|
committer | Steve Slaven <bpk@hoopajoo.net> | 2011-01-04 23:11:37 (GMT) |
commit | 27e58b98fa26d9f9986d9421bec61e0eb837fc07 (patch) | |
tree | 2861f5147ce8b697cbad417fc22f08605a131869 /src | |
parent | c8c2f024a5fd55cb570d744f965f23acc5744ced (diff) | |
download | SoftKeys-27e58b98fa26d9f9986d9421bec61e0eb837fc07.zip SoftKeys-27e58b98fa26d9f9986d9421bec61e0eb837fc07.tar.gz SoftKeys-27e58b98fa26d9f9986d9421bec61e0eb837fc07.tar.bz2 |
Moved main key sending stuff in to globals, added new smaller activity to
receive the intents, intents are generic now allowing sending any keycode
once the extras are set
Diffstat (limited to 'src')
-rw-r--r-- | src/net/hoopajoo/android/SoftKeys/Globals.java | 114 | ||||
-rw-r--r-- | src/net/hoopajoo/android/SoftKeys/Keys.java | 132 | ||||
-rw-r--r-- | src/net/hoopajoo/android/SoftKeys/SendInput.java | 109 | ||||
-rw-r--r-- | src/net/hoopajoo/android/SoftKeys/SoftKeysService.java | 93 |
4 files changed, 316 insertions, 132 deletions
diff --git a/src/net/hoopajoo/android/SoftKeys/Globals.java b/src/net/hoopajoo/android/SoftKeys/Globals.java index de6c76b..50f23a1 100644 --- a/src/net/hoopajoo/android/SoftKeys/Globals.java +++ b/src/net/hoopajoo/android/SoftKeys/Globals.java @@ -18,18 +18,27 @@ */ package net.hoopajoo.android.SoftKeys; +import java.io.File; +import java.io.FileOutputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import android.app.Application; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.preference.PreferenceManager; import android.provider.Settings; import android.util.Log; +import android.widget.Toast; public class Globals extends Application { private CommandShell cmd = null; private String android_id = null; + private String LOG = "SoftKeys.Global"; public boolean restartKeys = false; public int homeCounter = 0; @@ -45,7 +54,7 @@ public class Globals extends Application { // # cat /system/bin/sh > /data/tmp/su // # chmod 6755 /data/tmp/su // # mount -oremount,suid /dev/block/mtdblock1 /data - Log.d( "softkeys", "Detected emulator" ); + Log.d( LOG, "Detected emulator" ); cmd = new CommandShell( "/data/tmp/su" ); }else{ cmd = new CommandShell( "su" ); @@ -55,23 +64,61 @@ public class Globals extends Application { return( cmd ); } + // this is a string of keydown/keyup events by key id + public int sendKeys( List<Integer> a ) { + return sendKeys( listToInt( a ) ); + } + + public int sendKeys( int[] keyids ) { + try { + Globals.CommandShell cmd = getCommandShell(); + + // run our key script + String wd = getFilesDir().getAbsolutePath(); + + // check if we have a dev script + File script = new File( wd + "/pushkey.dev" ); + + // check if we have a test script + if( script.exists() ) { + Log.d( LOG, "Using dev key script" ); + }else{ + // write out our default script + script = new File( wd + "/pushkey" ); + FileOutputStream out = new FileOutputStream( script ); + out.write( "for f in $* ; do input keyevent $f ; done\n".getBytes( "ASCII" ) ); + out.close(); + } + + // source the file since datadata might be noexec + String keyid = ""; + cmd.system( "sh " + script.getAbsolutePath() + " " + keyid ); + }catch( Exception e ) { + Log.e( LOG, "Error: " + e.getMessage() ); + Toast.makeText( this, "Unable to execute as root", Toast.LENGTH_LONG ).show(); + return 1; + } + + return 0; + } + public class CommandShell { Process p; OutputStream o; CommandShell( String shell ) throws Exception { - Log.d( "softkeys.cmdshell", "Starting shell: '" + shell + "'" ); + Log.d( "SoftKeys.cmdshell", "Starting shell: '" + shell + "'" ); p = Runtime.getRuntime().exec( shell ); o = p.getOutputStream(); } public void system( String cmd ) throws Exception { - Log.d( "softkeys.cmdshell", "Running command: '" + cmd + "'" ); + Log.d( "SoftKeys.cmdshell", "Running command: '" + cmd + "'" ); o.write( (cmd + "\n" ).getBytes( "ASCII" ) ); } public void close() throws Exception { - Log.d( "softkeys.cmdshell", "Destroying shell" ); + Log.d( "SoftKeys.cmdshell", "Destroying shell" ); o.flush(); o.close(); p.destroy(); @@ -80,6 +127,19 @@ public class Globals extends Application { @Override public void onCreate() { + // warn if we don't notice some binaries we need + for( String name : new String[] { "/system/bin/su", "/system/bin/input" } ) { + File check = new File( name ); + try { + if( ! check.exists() ) { + Toast.makeText( this, "Failed to find file: " + name + ", SoftKeys may not function", Toast.LENGTH_LONG ).show(); + } + }catch( Exception e ) { + Toast.makeText( this, "Unable to check for file: " + name, Toast.LENGTH_LONG ).show(); + } + + } + android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID); restartService(); } @@ -92,4 +152,50 @@ public class Globals extends Application { this.startService( new Intent( this, SoftKeysService.class ) ); } } + + private int[] listToInt( List<Integer> a ) { + int[] ret = new int[ a.size() ]; + for( int i = 0; i < a.size(); i++ ) { + ret[ i ] = a.get( i ).intValue(); + } + return( ret ); + } + + public void doHomeAction( boolean longClick ) { + // special case + SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences( this ); + + Intent ni = new Intent( Intent.ACTION_MAIN ); + String launcher = settings.getString( longClick ? "launcher2" : "launcher" , null ); + if( launcher == null ) { + launcher = getDefaultLauncher(); + } + ni.setPackage( launcher ); + ni.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity( ni ); + } + + private String getDefaultLauncher() { + // Set default launcher to the first launcher we find so we don't freak out if it's not + // set and there is no com.android.launcher + Intent i = new Intent( Intent.ACTION_MAIN ); + i.addCategory( Intent.CATEGORY_HOME ); + PackageManager p = getPackageManager(); + List<ResolveInfo> packages = p.queryIntentActivities( i, 0 ); + + String defaultLauncher = null; + for( Iterator<ResolveInfo> it = packages.iterator(); it.hasNext(); ) { + ResolveInfo info = it.next(); + if( defaultLauncher == null ) { + if( ! info.activityInfo.applicationInfo.packageName.equals( "net.hoopajoo.android.SoftKeys" ) ) { + defaultLauncher = info.activityInfo.applicationInfo.packageName; + } + } + } + if( defaultLauncher == null ) { + // last ditch + defaultLauncher = "com.android.launcher"; + } + return( defaultLauncher ); + } } diff --git a/src/net/hoopajoo/android/SoftKeys/Keys.java b/src/net/hoopajoo/android/SoftKeys/Keys.java index c4776f7..5f7f8c1 100644 --- a/src/net/hoopajoo/android/SoftKeys/Keys.java +++ b/src/net/hoopajoo/android/SoftKeys/Keys.java @@ -20,8 +20,12 @@ package net.hoopajoo.android.SoftKeys; import java.io.File; import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import android.app.Activity; import android.app.Notification; @@ -69,12 +73,8 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen private Runnable delayed_pretap_action ; private String homeaction; private int delayed_action_time; - - public static String ACTION_MENU = "net.hoopajoo.android.SoftKeys.KEY_MENU"; - public static String ACTION_HOME = "net.hoopajoo.android.SoftKeys.KEY_HOME"; - public static String ACTION_BACK = "net.hoopajoo.android.SoftKeys.KEY_BACK"; - public static String ACTION_SEARCH = "net.hoopajoo.android.SoftKeys.KEY_SEARCH"; - + + // simple typedef used to make the notifications a bit more generic private class NotificationButton { String mPrefKey; @@ -83,18 +83,20 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen Drawable mIcon; String mButtonText; String mAction; + String mExtraString; - NotificationButton( String text, String pref, RemoteViews view, Drawable d, int icon, String act ) { + NotificationButton( String text, String pref, RemoteViews view, Drawable d, int icon, String act, String extra ) { mButtonText = text; mPrefKey = pref; mView = view; mIconId = icon; mIcon = d; mAction = act; + mExtraString = extra; } NotificationButton( String text, String pref, int icon, String act ) { - this( text, pref, null, null, icon, act ); + this( text, pref, null, null, icon, act, null ); } } @@ -261,29 +263,29 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen theme.getRemoteViews( new String[] { "notification_menu" } ), theme.getDrawable( new String[] { "notification_menu" } ), R.drawable.button_menu, - ACTION_MENU ); + SendInput.ACTION_CODE, "menu" ); nb[ 2 ] = new NotificationButton( "Home", "nb_home", theme.getRemoteViews( new String[] { "notification_home" } ), theme.getDrawable( new String[] { "notification_home" } ), R.drawable.button_home, - ACTION_HOME ); + SendInput.ACTION_CODE, "home" ); nb[ 3 ] = new NotificationButton( "Back", "nb_back", theme.getRemoteViews( new String[] { "notification_back" } ), theme.getDrawable( new String[] { "notification_back" } ), R.drawable.button_back, - ACTION_BACK ); + SendInput.ACTION_CODE, "back" ); nb[ 4 ] = new NotificationButton( "Search", "nb_search", theme.getRemoteViews( new String[] { "notification_search" } ), theme.getDrawable( new String[] { "notification_search" } ), R.drawable.button_search, - ACTION_SEARCH ); + SendInput.ACTION_CODE, "back" ); for( NotificationButton b : nb ) { if( settings.getBoolean( b.mPrefKey, false ) ) { Notification n = new Notification( b.mIconId, null, 0 ); - PendingIntent i = PendingIntent.getActivity( this, 0, - new Intent( b.mAction, - null, this, Keys.class ), 0 ); + Intent si = new Intent( b.mAction ); + si.putExtra( "keyname", b.mExtraString ); + PendingIntent i = PendingIntent.getActivity( this, 0, si, 0 ); // if we got a drawable but no view then set up our own remote view // and add in their drawable @@ -370,7 +372,8 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen }catch( Exception e ) { } - d( "Updating last version code: " + force_level ); + + //d( "Updating last version code: " + force_level ); if( settings.getInt( "last_intro_level", 0 ) < force_level ) { Intent intent = new Intent( this, QuickDoc.class ); intent.putExtra( "type", "whats_new" ); @@ -421,33 +424,6 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen return; } } - - ///////// TODO: remove null junk - - // handle real actions - if( i != null ) { - String action = i.getAction(); - if( action != null ) { - int clickbutton = 0; - if( action.equals( ACTION_MENU ) ) { - clickbutton = R.id.menu; - } - if( action.equals( ACTION_HOME ) ) { - clickbutton = R.id.home; - } - if( action.equals( ACTION_BACK ) ) { - clickbutton = R.id.back; - } - if( action.equals( ACTION_SEARCH ) ) { - clickbutton = R.id.search; - } - if( clickbutton != 0 ) { - generic_click( clickbutton, false, false ); - // don't draw the ui - this.finish(); - } - } - } if( isPaused ) { //d( "detected paused, resetting counter" ); @@ -524,22 +500,15 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen } private boolean generic_click( int id, boolean longClick, boolean backout ) { - String keyid = ""; + List<Integer> keyids = new ArrayList<Integer>(); switch( id ) { case R.id.back: - // If backout=true we are in softkeys main ui so honor return to softkeys - // by pressing home after this - keyid = "4"; + keyids.add( 4 ); break; case R.id.home: // do whatever is selected - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences( this ); - - Intent i = new Intent( Intent.ACTION_MAIN ); - i.setPackage( settings.getString( longClick ? "launcher2" : "launcher" , defaultLauncher ) ); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity( i ); + ((Globals)getApplication()).doHomeAction( longClick ); return true; case R.id.main_view: @@ -548,11 +517,11 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen return true; case R.id.menu: - keyid = "82"; + keyids.add( 82 ); break; case R.id.search: - keyid = "84"; + keyids.add( 84 ); break; case R.id.exit: @@ -560,52 +529,33 @@ public class Keys extends Activity implements OnClickListener, OnLongClickListen return true; default: - d( "Unkown click event: " + id ); + d( "Unknown click event: " + id ); return true; } + // if longclick then add another back, e.g. for apps that do something odd like pause when you + // open another app, so you can back out of that then send the intended key + if( longClick ) { + keyids.add( 0, 4 ); + } + + if( backout ) { + // if we need to back out of softkeys before we send the other keys + keyids.add( 0, 4 ); + } + + ((Globals)getApplication()).sendKeys( keyids ); + try { - Globals.CommandShell cmd = ((Globals)getApplication()).getCommandShell(); - - // run our key script - String wd = getFilesDir().getAbsolutePath(); - - // check if we have a dev script - File script = new File( wd + "/pushkey.dev" ); - - // check if we have a test script - if( script.exists() ) { - d( "Using dev key script" ); - }else{ - // write out our default script - script = new File( wd + "/pushkey" ); - FileOutputStream out = new FileOutputStream( script ); - out.write( "for f in $* ; do input keyevent $f ; done\n".getBytes( "ASCII" ) ); - out.close(); - } - - // if longclick then add another back, e.g. for apps that do something odd like pause when you - // open another app, so you can back out of that then send the intended key - if( longClick ) { - keyid = "4 " + keyid; - } - - if( backout ) { - // if we need to back out of softkeys before we send the other keys - keyid = "4 " + keyid; - } - - // source the file since datadata might be noexec - cmd.system( "sh " + script.getAbsolutePath() + " " + keyid ); - // if we sent back, and didn't backout (so it was from main ui) and they // want to return, run am to get us back if( id == R.id.back && backout && return_after_back ) { + Globals.CommandShell cmd = ((Globals)getApplication()).getCommandShell(); cmd.system( "am start -a android.intent.action.MAIN -n net.hoopajoo.android.SoftKeys/.Keys" ); } }catch( Exception e ) { - Log.e( LOG, "Error: " + e.getMessage() ); - Toast.makeText( this, "Unable to execute as root", Toast.LENGTH_LONG ).show(); + // we don't really care if this fails, they should have gotten a shell + // error from the sendkeys } return true; diff --git a/src/net/hoopajoo/android/SoftKeys/SendInput.java b/src/net/hoopajoo/android/SoftKeys/SendInput.java new file mode 100644 index 0000000..9037e82 --- /dev/null +++ b/src/net/hoopajoo/android/SoftKeys/SendInput.java @@ -0,0 +1,109 @@ +/* + * + * Copyright (c) 2010 Steve Slaven + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * +*/ +package net.hoopajoo.android.SoftKeys; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.View.OnTouchListener; +import android.widget.ImageButton; +import android.widget.RemoteViews; +import android.widget.Toast; + +// this is just a stub to handle intent calls +public class SendInput extends Activity { + public static String ACTION_CODE = "net.hoopajoo.android.SoftKeys.KEY_CODE"; + + private static final Map<String,Integer> mKeymap; + static { + Map<String,Integer> t = new HashMap<String,Integer>(); + t.put( "back", 4 ); + t.put( "menu", 82 ); + t.put( "search", 84 ); + mKeymap = Collections.unmodifiableMap( t ); + } + + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + onNewIntent( getIntent() ); + } + + @Override + public void onNewIntent( Intent i ) { + Globals app = (Globals)getApplication(); + + String action = i.getAction(); + if( action.equals( ACTION_CODE ) ) { + // by key name? + int keyid = 0; + Bundle e = i.getExtras(); + boolean longClick = e.getBoolean( "longclick", false ); + if( e.getString( "keyname" ) != null ) { + String key = e.getString( "keyname" ); + if( key.equals( "home" ) ) { + ((Globals)getApplication()).doHomeAction( longClick ); + return; + }else{ + // run through resolver + if( mKeymap.containsKey( key ) ) { + keyid = mKeymap.get( key ); + } + } + } + + ((Globals)getApplication()).sendKeys( new int[] { keyid } ); + // todo: make me a broadcast receiver + this.finish(); + } + } +} diff --git a/src/net/hoopajoo/android/SoftKeys/SoftKeysService.java b/src/net/hoopajoo/android/SoftKeys/SoftKeysService.java index 1118406..bdfe63e 100644 --- a/src/net/hoopajoo/android/SoftKeys/SoftKeysService.java +++ b/src/net/hoopajoo/android/SoftKeys/SoftKeysService.java @@ -71,43 +71,14 @@ public class SoftKeysService extends Service { OnClickListener c = new OnClickListener() { @Override public void onClick( View v ) { - // send an intent to the main window - Intent i = null; - boolean hide = auto_hide; - switch( v.getId() ) { - case R.id.home: - i = new Intent( Keys.ACTION_HOME ); - break; - - case R.id.back: - i = new Intent( Keys.ACTION_BACK ); - if( hide ) { - hide = auto_hide_after_back; - } - break; - - case R.id.menu: - i = new Intent( Keys.ACTION_MENU ); - break; - - case R.id.search: - i = new Intent( Keys.ACTION_SEARCH ); - break; - - case R.id.exit: - hide = true; - break; - } - - if( i != null ) { - i.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); - i.setClass( v.getContext(), Keys.class ); - v.getContext().startActivity( i ); - } - - if( hide ) { - toggle_bar(); - } + genericClick( v, false ); + } + }; + + OnLongClickListener lc = new OnLongClickListener() { + @Override + public boolean onLongClick( View v ) { + return genericClick( v, true ); } }; @@ -416,6 +387,54 @@ public class SoftKeysService extends Service { } + private boolean genericClick( View v, boolean longclick) { + // send an intent to the main window + Intent i = null; + boolean hide = auto_hide; + switch( v.getId() ) { + case R.id.home: + i = new Intent( SendInput.ACTION_CODE ); + i.putExtra( "keyname", "home" ); + break; + + case R.id.back: + i = new Intent( SendInput.ACTION_CODE ); + i.putExtra( "keyname", "back" ); + if( hide ) { + hide = auto_hide_after_back; + } + break; + + case R.id.menu: + i = new Intent( SendInput.ACTION_CODE ); + i.putExtra( "keyname", "menu" ); + break; + + case R.id.search: + i = new Intent( SendInput.ACTION_CODE ); + i.putExtra( "keyname", "search" ); + break; + + case R.id.exit: + hide = true; + break; + } + + if( i != null ) { + i.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK ); + i.putExtra( "long", longclick ); + + //i.setClass( v.getContext(), Keys.class ); + v.getContext().startActivity( i ); + } + + if( hide ) { + toggle_bar(); + } + + return true; + } + @Override public void onDestroy() { super.onDestroy(); |