Sunday, November 7, 2010

Invisible activity - Why status bar is not focusable ?

I was in a situation to launch a keyboard, when a tap happens on the edittext on the status bar.
I introduced a editText in the status bar. And expected framework to launch the keyboard when tapped. But unfortunately framework doesnt popup the keyboard.

I posted in android-developers forums and got some clue by android framework engineers.

1 - When status bar view is added to windowmanager, windowManager layoutparams is set with the flag 'WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE '.

frameworks/base/services/java/com/android/server/status/StatusBarService.java



public void systemReady() {
System.out.println("Statusbar service - systemReady ");
final StatusBarView view = mStatusBarView;
WindowManager.LayoutParams lp = new
WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
view.getContext().getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height),
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
mPixelFormat);
lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
lp.setTitle("StatusBar");
lp.windowAnimations = R.style.Animation_StatusBar;
WindowManagerImpl.getDefault().addView(view, lp);
}


FLAG_NOT_FOCUSABLE Window flag: this window won't ever get key
input focus, so the user can not send key or other button events to
it.

http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_NOT_FOCUSABLE

2 - It is decided by the framework that status bar will not receive any focusable events. You can handle the touch, but it will not get any focus.!!

- I tried uncommenting that flag and checked. Then status bar window received focus, where as the other windows mainly - phone Window, where the activity window resides doesnt receive focus, so literally i couldnt do anything on the screen....:)

for more details refer here

http://groups.google.com/group/android-platform/browse_thread/thread/385fa0ede79fd7f8/e286f2ff0e6f1c16#e286f2ff0e6f1c16

So i decided to a write a invisible activity :)

Means like, if somebody taps on the status bar's edit text, i launch an activity and through that activity i launch the keyboard, giving user an illusion that keyboard is launched because of the tap on the edit text.

how do we achieve this ???

- To pop up the keyboard automatically when the activity is launched, we need a editText component in the activity.

- A runnable is created and it will be executed after the activity is launched to show the inputmethod (IME) using inputManager.Which gives user a illusion that keyboard is launched. Keyboard can be launched with any View element, Button, TextView, EditText..etc...But View has to be in foucs



@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Launch the IME after a bit
mHandler.postDelayed(mShowInputMethodTask, 0); --> Launch the runnable
}
}

private Runnable mShowInputMethodTask = new Runnable() { -->Runnable
public void run() {
showInputMethodForQuery();
}
};

protected void showInputMethodForQuery() { -----> Shows the IME using a View
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(press, 0);
}
}



Lets see the complete example:

1- Create a simple View element in the layout.xml.. In this case i have created a button.

To make it invisible.

Apply the background, text property to color:transparent

android:textColor="@android:color/transparent"
android:background="@android:color/transparent"


Layout.xml:
------------



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>

<Button android:id="@+id/press"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/transparent"
android:background="@android:color/transparent"
android:text="@string/hello"/>

</LinearLayout>



Note:

When the keyboard is launched and when the user presses 'back' button, there is no way that inputmethodManager will let the application know that keyboard is exited.
So when back is pressed, keyboard will be dismissed and you could see a transparent window, where u cannot really do anything.

Basically it is the transparent activity which has button in it which is also a transparent one due to the settings of background color & text color.

So we need to handle the onTouchEvent() of activity and do an exit. ( this.finish() )



invisibleactivity.java
--------------------------



package com.android.urldisplay;

import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.view.Display;
import android.widget.LinearLayout;
import android.view.ViewGroup;
import android.view.Gravity;

public class invisibleactivity extends Activity
{

private static final int APP_ID = 0;
private Handler mHandler = new Handler();
Button press;

private Runnable mShowInputMethodTask = new Runnable() {
public void run() {
showInputMethodForQuery();
}
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);


}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Launch the IME after a bit
mHandler.postDelayed(mShowInputMethodTask, 0);
}
}
protected void showInputMethodForQuery() {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(press, 0);
}
}
public boolean onTouchEvent (MotionEvent event)
{
this.finish();
return true;
}
}




- In this approach we only launched the keyboard. But our aim was to handle the text typed in the keyboard and show it back to the editText on the status bar.

So for that instead of button we need to have a editText and launch the keyboard for this view (EditText ). and when the user presses 'Go' or 'Done' button get the text from this editText.

Will show you how to handle the button press on keyboard in coming blogs :)

1 comment: