package com.android.systemui.statusbar.policy; import java.net.URISyntaxException; import android.app.Activity; import android.app.ActivityManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.hardware.input.InputManager; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import android.view.ViewConfiguration; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.statusbar.policy.KeyButtonView; import com.android.systemui.R; public class ExtensibleKeyButtonView extends KeyButtonView { final static String ACTION_HOME = "**home**"; final static String ACTION_BACK = "**back**"; final static String ACTION_SEARCH = "**search**"; final static String ACTION_MENU = "**menu**"; final static String ACTION_POWER = "**power**"; final static String ACTION_NOTIFICATIONS = "**notifications**"; final static String ACTION_RECENTS = "**recents**"; final static String ACTION_IME = "**ime**"; final static String ACTION_KILL = "**kill**"; final static String ACTION_NULL = "**null**"; final static String ACTION_WIDGETS = "**widgets**"; private static final String TAG = "Key.Ext"; IStatusBarService mBarService; public String mClickAction, mLongpress; public Handler mHandler; public ActivityManager mActivityManager; public int mInjectKeycode; public ExtensibleKeyButtonView(Context context, AttributeSet attrs) { this(context, attrs, ACTION_NULL,ACTION_NULL); } public ExtensibleKeyButtonView(Context context, AttributeSet attrs, String ClickAction, String Longpress) { super(context, attrs); mHandler = new Handler(); mActivityManager = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mClickAction = ClickAction; mLongpress = Longpress; setCode(0); if (ClickAction != null){ if (ClickAction.equals(ACTION_HOME)) { setCode(KeyEvent.KEYCODE_HOME); setId(R.id.home); } else if (ClickAction.equals(ACTION_BACK)) { setCode (KeyEvent.KEYCODE_BACK); setId(R.id.back); } else if (ClickAction.equals(ACTION_SEARCH)) { setCode (KeyEvent.KEYCODE_SEARCH); } else if (ClickAction.equals(ACTION_MENU)) { setCode (KeyEvent.KEYCODE_MENU); } else if (ClickAction.equals(ACTION_POWER)) { setCode (KeyEvent.KEYCODE_POWER); } else { // the remaining options need to be handled by OnClick; setOnClickListener(mClickListener); if (ClickAction.equals(ACTION_RECENTS)) setId(R.id.recent_apps); } } setSupportsLongPress (false); if (Longpress != null) if ((!Longpress.equals(ACTION_NULL)) || (getCode() !=0)) { // I want to allow long presses for defined actions, or if // primary action is a 'key' and long press isn't defined otherwise setSupportsLongPress(true); setOnLongClickListener(mLongPressListener); } } public void injectKeyDelayed(int keycode){ mInjectKeycode = keycode; mHandler.removeCallbacks(onInjectKey_Down); mHandler.removeCallbacks(onInjectKey_Up); mHandler.post(onInjectKey_Down); mHandler.postDelayed(onInjectKey_Up,10); // introduce small delay to handle key press } final Runnable onInjectKey_Down = new Runnable() { public void run() { final KeyEvent ev = new KeyEvent(mDownTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, mInjectKeycode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } }; final Runnable onInjectKey_Up = new Runnable() { public void run() { final KeyEvent ev = new KeyEvent(mDownTime, SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, mInjectKeycode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); } }; Runnable mKillTask = new Runnable() { public void run() { String packageName = mActivityManager.getRunningTasks(1).get(0).topActivity.getPackageName(); mActivityManager.forceStopPackage(packageName); } }; private OnClickListener mClickListener = new OnClickListener() { @Override public void onClick(View v) { // the other consts were handled by keycode. if (mClickAction.equals(ACTION_NULL)) { // who would set a button with no ClickAction? // Stranger things have happened. return; } else if (mClickAction.equals(ACTION_RECENTS)) { try { mBarService.toggleRecentApps(); } catch (RemoteException e) { } return; } else if (mClickAction.equals(ACTION_NOTIFICATIONS)) { try { mBarService.toggleNotificationShade(); } catch (RemoteException e) { // A RemoteException is like a cold // Let's hope we don't catch one! } return; } else if (mClickAction.equals(ACTION_IME)) { getContext().sendBroadcast(new Intent("android.settings.SHOW_INPUT_METHOD_PICKER")); return; } else if (mClickAction.equals(ACTION_KILL)) { mHandler.postDelayed(mKillTask,ViewConfiguration.getGlobalActionKeyTimeout()); return; } else if (mClickAction.equals(ACTION_WIDGETS)) { // Widgets not yet imported to JB - Zaphod 07/21/12 return; /*Intent toggleWidgets = new Intent( NavigationBarView.WidgetReceiver.ACTION_TOGGLE_WIDGETS); mContext.sendBroadcast(toggleWidgets); */ } else { // we must have a custom uri try { Intent intent = Intent.parseUri(mClickAction, 0); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); } catch (URISyntaxException e) { Log.e(TAG, "URISyntaxException: [" + mClickAction + "]"); } catch (ActivityNotFoundException e){ Log.e(TAG, "ActivityNotFound: [" + mClickAction + "]"); } } return; } }; private OnLongClickListener mLongPressListener = new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mLongpress == null) { return true; } if (mLongpress.equals(ACTION_NULL)) { // attempt to keep long press functionality of 'keys' if // they haven't been overridden. return true; } else if (mLongpress.equals(ACTION_HOME)) { injectKeyDelayed(KeyEvent.KEYCODE_HOME); return true; } else if (mLongpress.equals(ACTION_BACK)) { injectKeyDelayed(KeyEvent.KEYCODE_BACK); return true; } else if (mLongpress.equals(ACTION_SEARCH)) { injectKeyDelayed(KeyEvent.KEYCODE_SEARCH); return true; } else if (mLongpress.equals(ACTION_MENU)) { injectKeyDelayed(KeyEvent.KEYCODE_MENU); return true; } else if (mLongpress.equals(ACTION_POWER)) { injectKeyDelayed(KeyEvent.KEYCODE_POWER); return true; } else if (mLongpress.equals(ACTION_IME)) { getContext().sendBroadcast(new Intent("android.settings.SHOW_INPUT_METHOD_PICKER")); return true; } else if (mLongpress.equals(ACTION_KILL)) { mHandler.post(mKillTask); return true; } else if (mLongpress.equals(ACTION_WIDGETS)) { // Widgets not yet imported to JB - Zaphod 07/21/12 return true; /* Intent toggleWidgets = new Intent( NavigationBarView.WidgetReceiver.ACTION_TOGGLE_WIDGETS); mContext.sendBroadcast(toggleWidgets); return true; */ } else if (mLongpress.equals(ACTION_RECENTS)) { try { mBarService.toggleRecentApps(); } catch (RemoteException e) { // let it go. } return true; } else if (mClickAction.equals(ACTION_NOTIFICATIONS)) { try { mBarService.toggleNotificationShade(); } catch (RemoteException e) { // A RemoteException is like a cold // Let's hope we don't catch one! } return true; } else { // we must have a custom uri try { Intent intent = Intent.parseUri(mLongpress, 0); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); } catch (URISyntaxException e) { Log.e(TAG, "URISyntaxException: [" + mLongpress + "]"); } catch (ActivityNotFoundException e){ Log.e(TAG, "ActivityNotFound: [" + mLongpress + "]"); } return true; } } }; }