summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml17
-rw-r--r--src/net/hoopajoo/android/SoftKeys/Globals.java114
-rw-r--r--src/net/hoopajoo/android/SoftKeys/Keys.java132
-rw-r--r--src/net/hoopajoo/android/SoftKeys/SendInput.java109
-rw-r--r--src/net/hoopajoo/android/SoftKeys/SoftKeysService.java93
5 files changed, 327 insertions, 138 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 85ac68a..bf14161 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -33,12 +33,6 @@
<category android:name="android.intent.category.MONKEY" />
</intent-filter>
- <intent-filter>
- <action android:name="net.hoopajoo.SoftKeys.KEY_MENU" />
- <action android:name="net.hoopajoo.SoftKeys.KEY_HOME" />
- <action android:name="net.hoopajoo.SoftKeys.KEY_BACK" />
- <action android:name="net.hoopajoo.SoftKeys.KEY_SEARCH" />
- </intent-filter>
</activity>
<activity
@@ -51,7 +45,18 @@
android:configChanges="orientation|keyboardHidden"
android:theme="@android:style/Theme.Dialog">
</activity>
+
+
+ <activity
+ android:name=".SendInput"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="net.hoopajoo.android.SoftKeys.KEY_CODE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<service android:name=".SoftKeysService"/>
</application>
<uses-sdk android:minSdkVersion="4" />
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();