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);
}
Related
Below code snippet only shows view but click on buttons not working. I have tried all solution but nothing seems to work. Another service have same code fragment with different layout works.
public int onStartCommand(final Intent intent, int flags, int startId) {
LayoutInflater inflater = (LayoutInflater)getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
if (inflater!=null)
v = inflater.inflate(R.layout.layout_accept_sos, null);
close = v.findViewById(R.id.close);
close.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
onDestroy();
}
});
getDirection = v.findViewById(R.id.getDirection);
getDirection.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent1=new Intent();
intent1.setAction("open_map");
sendBroadcast(intent1);
// onDestroy();
}
});
params = new WindowManager.LayoutParams(LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = 0;
params.y = 0;
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
wm.addView(v, params);
return START_STICKY;
}
You need to use WindowManager.FLAG_NOT_FOCUSABLE to make it clickable.
params = new WindowManager.LayoutParams(LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
More description available at https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_NOT_FOCUSABLE
ImageView click is not working on custom layout creatd in android service.
public class CallService extends Service {
String incomingnumber;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
incomingnumber = intent.getExtras().getString("incomingnumber");
System.out.println("=== onStartCommand incomingnumber : " + incomingnumber);
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onCreate() {
super.onCreate();
final Context context = getApplicationContext();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup mView = (ViewGroup) inflater.inflate(R.layout.testactivity, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
ImageView imgCallPick = mView.findViewById(R.id.imgCallPick);
ImageView imgCaller = mView.findViewById(R.id.imgCaller);
imgCaller.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("===== imgCaller");
return false;
}
});
wm.addView(mView, params);
} }
i just create custom layout in service, the layout contain a diff view but the Imageview click not working. is there any solution for that, then help me.
Thanks
From this answer: Android - ImageView On Click
Add in your xml layout:
android:clickable="true"
This issue is due to use of deprecated param
WindowManager.LayoutParams.TYPE_PHONE
For getting touch events
Use this if you want a colored background
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.RGB_565);
Use this if you want a transparent background
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSPARENT);
Everything else remains same :)
here is the service that i have called to display view above locked screen.i dont want to unlock lock screen i want something that whatsapp and skype use for their calling screen.such that screen is not unlocked.
public class Display extends Service {
private static WindowManager wm;
private static RelativeLayout ly;
private WindowManager.LayoutParams params;
#Override
public void onCreate() {/
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
LayoutInflater vi;
vi = LayoutInflater.from(this);
ly = (RelativeLayout) vi.inflate(R.layout.display, null);
wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
/*ALSO TRIED USING WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON*/
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.RGBA_8888);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 300;
ly.setLayoutParams(params);
TextView tv = (TextView) ly.findViewById(R.id.annotationTextView);
tv.setText("check");
wm.addView(ly, params);
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
}
}
but the screen was appearing below locked screen.
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
// WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
PixelFormat.RGBA_8888);
worked for me but i was not able to touch on it so still unsuccessful
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 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