I want to create a floating action button that keep running like DU Speed Booster.
Something like iOS assistive touch.
Here is a picture.
For doing this you might need to have this permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Create a Service and put this code inside OnCreate()
public class ChatHeadService extends Service {
private WindowManager windowManager;
private ImageView chatHead;
#Override public IBinder onBind(Intent intent) {
// Not used
return null;
}
#Override public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setImageResource(R.drawable.android_head);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 100;
windowManager.addView(chatHead, params);
}
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null) windowManager.removeView(chatHead);
}
}
Start serviceusing below code
startService(new Intent(context, ChatHeadService.class));
Please refer this blog for complete example http://androidsrc.net/facebook-chat-like-floating-chat-heads/
Related
I am trying to make a Android app with a service (Floating widget). Since I am not a pro-programmer, I did not use touch listener to change position of that widget for lot of reasons. But I want user to have options to change position (some preferred locations only) of that widget, and that options will be available in an activity which invoked the service. I am passing data as intent extra from my activity to service, which is to be obtained from service onStartCommand(). The problem I am facing now- I am unable to use data obtained from onStartCommand() in onCreate(). Seems the service is created before onStartCommand() is called and I am getting null pointer exception as some of my variables are not obtaining values from onStartCommand(). I have checked, that these variables are getting values when I use any clickable objects (e.g: press any button view in service), I used a dummy toast for this.
Please share your valuable opinion on how to overcome this problem.
public class MainFloatingWidget extends Service {
private WindowManager windowManager;
private View myFloatingView, myCollapsedView, myExpandedView;
private WindowManager.LayoutParams params;
private String position;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
myFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_main_floating_widget, null);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(myFloatingView, params);
myCollapsedView = myFloatingView.findViewById(R.id.collapsedViewMainService);
myExpandedView = myFloatingView.findViewById(R.id.expandedViewMainService);
myCollapsedView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
myCollapsedView.setVisibility(View.GONE);
myExpandedView.setVisibility(View.VISIBLE);
return true;
}
});
myExpandedView.findViewById(R.id.buttonBack).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myCollapsedView.setVisibility(View.VISIBLE);
myExpandedView.setVisibility(View.GONE);
}
});
myExpandedView.findViewById(R.id.buttonClose).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
windowManager.removeView(myFloatingView);
stopSelf();
}
});
if(position.equals("Right-Middle")){
params.gravity = Gravity.END | Gravity.CENTER;
}else if(position.equals("Right-Top")){
params.gravity = Gravity.END | Gravity.TOP;
}else if(position.equals("Right-Bottom")){
params.gravity = Gravity.END | Gravity.BOTTOM;
}else if(position.equals("Left-Middle")){
params.gravity = Gravity.START | Gravity.CENTER;
}else if(position.equals("Left-Top")){
params.gravity = Gravity.START | Gravity.TOP;
}else if(position.equals("Left-Bottom")){
params.gravity = Gravity.START | Gravity.BOTTOM;
}else{
params.gravity = Gravity.CENTER;
}
windowManager.updateViewLayout(myFloatingView, params);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
position = (String) intent.getExtras().get("POSITION");
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
onCreate will call before onStartCommand and only call one time when Service init so you should move your code handle position to inside onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
position = (String) intent.getExtras().get("POSITION");
if(position.equals("Right-Middle")){
params.gravity = Gravity.END | Gravity.CENTER;
}else if(position.equals("Right-Top")){
params.gravity = Gravity.END | Gravity.TOP;
}else if(position.equals("Right-Bottom")){
params.gravity = Gravity.END | Gravity.BOTTOM;
}else if(position.equals("Left-Middle")){
params.gravity = Gravity.START | Gravity.CENTER;
}else if(position.equals("Left-Top")){
params.gravity = Gravity.START | Gravity.TOP;
}else if(position.equals("Left-Bottom")){
params.gravity = Gravity.START | Gravity.BOTTOM;
}else{
params.gravity = Gravity.CENTER;
}
windowManager.updateViewLayout(myFloatingView, params);
return super.onStartCommand(intent, flags, startId);
}
I'm trying to create a custom chatHead which would show only when a particular app is open. (e.g. Messenger or WhatsApp). And would be destroyed when that app has been closed.
Here's my code-
#Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setImageResource(R.drawable.view_icon);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 100;
windowManager.addView(chatHead, params);
getForeground();
chatHead.setOnClickListener(new View.OnClickListener() {
/*.....
.....
ClickListener implemented
.....
.....
*/
});
}
Here, in the onCreate(), I'm calling the getForeground() method, which gets the current activity in the foreground
public String getForeground() {
final ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
String foreground = am.getRunningTasks(1).get(0).topActivity.getPackageName();
if (foreground.equals("com.whatsapp")) {
if (chatHead==null)
onResume();
}
else
onDestroy();
new Thread() {
#Override
public void run() {
getForeground();
}
}.start();
return foreground;
}
Where, onResume() is-
public void onResume() {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 100;
windowManager.addView(chatHead,params);
}
And onDestroy() is-
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null)
windowManager.removeView(chatHead);
else
chatHead=null;
}
However, it shows an IllegalStateException as follows-
java.lang.IllegalArgumentException: View=android.widget.ImageView{41f07a28 V.ED..C. ......ID 0,0-93,99} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:370)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:299)
at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:79)
at automated.whatsapp.com.automatedtest.ChatHeadService.onDestroy(ChatHeadService.java:172)
at automated.whatsapp.com.automatedtest.ChatHeadService.getForeground(ChatHeadService.java:188)
at automated.whatsapp.com.automatedtest.ChatHeadService$3.run(ChatHeadService.java:194)
So, basically, it says that the chatHead is not attached to the WindowManager. How do I correct this error?
You can use try-catch around your window manager like this
try{
WindowManager?.removeView(view);
}catch(e:Exception){
Log.e(debug_tag, "view not found");
}
or can check windowtoken of view exist
if (view?.windowToken!=null) {
WindowManager?.removeView(view);
}
because when your view is not added or remove view later your windowmanager make a new exception
Not sure if this can help you but this is what I did:
#Override
protected void onUserLeaveHint() {
if (this.chatHead != null) {
this.runOnUiThread(new Runnable() {
#Override
public void run() {
((WindowManager) getApplicationContext().getSystemService(Service.WINDOW_SERVICE)).removeView(chatHead);
}
});
}
}
I've created a service that creates a chatHead (like Facebook Messenger).
However, I want it to have a listener that always checks which activity or application is in the foreground.
Here's my code-
public class ChatHeadService extends Service {
private WindowManager windowManager;
private ImageView chatHead;
protected String lastPackageName;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
String foreground = am.getRunningTasks(1).get(0).topActivity.getPackageName();
if(foreground.equals("com.whatsapp"))
Toast.makeText(getApplicationContext(), foreground, Toast.LENGTH_SHORT).show();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
chatHead = new ImageView(this);
chatHead.setImageResource(R.drawable.view_icon);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 100;
windowManager.addView(chatHead, params);
}
#Override
public void onDestroy() {
super.onDestroy();
if (chatHead != null) windowManager.removeView(chatHead);
if(lastPackageName!="com.whatsapp") windowManager.removeView(chatHead);
}
}
Using the ActivityManager, it does work when it is created. But I guess, since it is in the OnCreate method, it only gets the foreground activity when it is created.
What I need is to have a listener that would detect if the activity in the foreground is some specific activity (like in this case, Whatsapp).
How do I do that?
Thanks
I hava a app, it will receive msg from server and make a dialog to user.So when phone is on lock screen i want dialog will show on the top of lock screen but not unlock it.could anyone give me suggestion ?
I solved something similar in the following way.
Create services, to broadcast the action "ACTION_SCREEN_ON & ACTION_USER_PRESENT & ACTION_SCREEN_OFF", create function to show the windows with WINDOW_SERVICE. I use a service for my requirements, but it can adapt.
public class OverlayService extends Service {
private static final String TAG = OverlayService.class.getSimpleName();
WindowManager mWindowManager;
View mView;
Animation mAnimation;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
registerOverlayReceiver();
return super.onStartCommand(intent, flags, startId);
}
private void showDialog(String aTitle){
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mView = View.inflate(getApplicationContext(), R.layout.fragment_overlay, null);
mView.setTag(TAG);
int top = getApplicationContext().getResources().getDisplayMetrics().heightPixels / 2;
RelativeLayout dialog = (RelativeLayout) mView.findViewById(R.id.dialog);
LayoutParams lp = (LayoutParams) dialog.getLayoutParams();
lp.topMargin = top;
lp.bottomMargin = top;
mView.setLayoutParams(lp);
ImageButton imageButton = (ImageButton) mView.findViewById(R.id.close);
lp = (LayoutParams) imageButton.getLayoutParams();
lp.topMargin = top - 58;
imageButton.setLayoutParams(lp);
imageButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mView.setVisibility(View.INVISIBLE);
}
});
TextView title = (TextView) mView.findViewById(R.id.Title);
title.setText(aTitle);
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ,
PixelFormat.RGBA_8888);
mView.setVisibility(View.VISIBLE);
mAnimation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.in);
mView.startAnimation(mAnimation);
mWindowManager.addView(mView, mLayoutParams);
}
private void hideDialog(){
if(mView != null && mWindowManager != null){
mWindowManager.removeView(mView);
mView = null;
}
}
#Override
public void onDestroy() {
unregisterOverlayReceiver();
super.onDestroy();
}
private void registerOverlayReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(overlayReceiver, filter);
}
private void unregisterOverlayReceiver() {
hideDialog();
unregisterReceiver(overlayReceiver);
}
private BroadcastReceiver overlayReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "[onReceive]" + action);
if (action.equals(Intent.ACTION_SCREEN_ON)) {
showDialog("Esto es una prueba y se levanto desde");
}
else if (action.equals(Intent.ACTION_USER_PRESENT)) {
hideDialog();
}
else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
hideDialog();
}
}
};
}
I hope it useful!
public void onAttachedToWindow() {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}
Finally I achieved the same. Don't go for activity, because android will not show lock screen behind your activity for security reason, so use service instead of Activity.
Below is my code in onStartCommand of my service
WindowManager mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
View mView = mInflater.inflate(R.layout.score, null);
WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
/* | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON */,
PixelFormat.RGBA_8888);
mWindowManager.addView(mView, mLayoutParams);
And add <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> to manifest
I have created an application that runs a service. The service basically has an Overlay button and the button on click does something. However, everytime, I click on the button, the application in the background of the button is responding. Please can someone help?
I have referred to link.
Here is my code:
Service code:
public class HUD extends Service implements OnTouchListener{
Button mButton;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
String s;
s = "Hari";
return null;
}
#Override
public void onCreate() {
super.onCreate();
//mView = new HUDView(this);
mButton = new Button(this);
mButton.setText("Overlay button");
mButton.setOnTouchListener(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.setTitle("Load Average");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mButton, params);
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getBaseContext(),"onDestroy", Toast.LENGTH_LONG).show();
if(mButton != null)
{
((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(mButton);
mButton = null;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(this,"Overlay button event", Toast.LENGTH_SHORT).show();
return false;
}
}
The manifest has the following permission as well:
I tried changing the types to
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
Now, wherever I click, the touch event is getting called. Please can someone help me?
The problem is you are using WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY. I think since some Android 4.x it is not usable anymore. This is the way i realized an overlay Button:
public class OverlayButton extends Service implements OnTouchListener {
Button mButton;
WindowManager wm;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mButton = new Button(this);
mButton.setText("Overlay button");
mButton.setTextColor(Color.BLACK);
mButton.setOnTouchListener(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(150,
150, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.CENTER;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mButton, params);
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getBaseContext(), "onDestroy", Toast.LENGTH_LONG).show();
if (mButton != null) {
((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(mButton);
mButton = null;
}
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
Log.d("OverlayButton onTouch", "touched the button");
stopSelf();
}
return true;
}
}
What i did is, i also gave him the ability to disappear again, if i touch him.
This is the way i let him appear:
getActivity().startService(new Intent(getActivity().getBaseContext(), OverlayButton.class));
but, if you start it from your Activity, just use:
startService(new Intent(this, OverlayButton.class));
And don't forget to set these in your manifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
And inside the tags also don't forget:
<service android:name="OverlayButton" ></service>
When you use this WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH flag, it is telling the app to watch for all touches outside of your view.
Try using WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL instead. This will intercept touches only on your view.
To confirm, you need to be using WindowManager.LayoutParams.TYPE_SYSTEM_ALERT to make the view clickable.