Im developing an app where a floating component should appear when the call is received and the component will have several buttons to perform necessary actions.
I have tried the follow.
I implemented a popup window by making the main activity translucent.when this component pops up, Im able to move it on the screen, but since the activity is translucent, im not able to perform any other activity.
here u can see the popup window, i can move it, but i cannot scroll the menudrawer in the background. How can i implement in such a way that i can perform both operations, i.e on popupwindow and the background screen.
My codes
`public class MainActivity extends Activity {
int mCurrentX;
int mCurrentY;
private float mDx;
private float mDy;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
final View cv = new View(this);
TextView tv = new TextView(this);
tv.setBackgroundColor(0xffeeeeee);
tv.setTextColor(0xff000000);
tv.setTextSize(24);
tv.setText("click me\nthen drag me");
tv.setPadding(8, 8, 8, 8);
final PopupWindow mPopup = new PopupWindow(tv, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
OnTouchListener otl = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mDx = mCurrentX - event.getRawX();
mDy = mCurrentY - event.getRawY();
} else
if (action == MotionEvent.ACTION_MOVE) {
mCurrentX = (int) (event.getRawX() + mDx);
mCurrentY = (int) (event.getRawY() + mDy);
mPopup.update(mCurrentX, mCurrentY, -1, -1);
}
return true;
}
};
tv.setOnTouchListener(otl);
mCurrentX = 20;
mCurrentY = 50;
cv.post(new Runnable() {
#Override
public void run() {
mPopup.showAtLocation(cv, Gravity.NO_GRAVITY, mCurrentX, mCurrentY);
}
});
}
}`
manifest
`<application android:label="#string/app_name" android:icon="#drawable/ic_launcher">
<activity android:name=".MainActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar"
android:label="#string/app_name"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="PopupMainActivity"
android:label="#string/app_name"
android:theme="#style/Theme.FloatingWindow.Popup"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:clearTaskOnLaunch="true"
android:exported="true"
tools:ignore="ExportedActivity" />
</application>`
please help me on this. I want to implement a widget kind of component on the whole. Thanks!!
You have to start a foreground Service and draw using the WindowManager on top of everything else, manage your popup position, size, etc...
WindowManger windowManager = Context.getSystemService(Context.WINDOW_SERVICE);
You will also have to add this permission "android.permission.SYSTEM_ALERT_WINDOW" to your manifest.
A simpler solution would be to use a library called StandOut, it basically takes care of all that I mentioned above and provide extra features like:
Window decorators (titlebar, minimize/close buttons, border, resize handle)
Windows are moveable and resizable. You can bring-to-front, minimize,
and close
Minimized windows can be restored (the example APK demos this using
the notification panel)
Create multiple types of windows, and multiple windows of each type
Related
I am programmatically setting the x value of an edittext. However, in doing so the adjust pan function does not work. Any ideas on how to make this work. The activity is not in full-screen mode and this only happens in APIs > 23.
Ex.
Edittext editext = findViewById(R.id.edittext);
edittext.setX(200);
//Clicking on edittext now, gets covered by the soft keyboard if it low enough on screen
Note: this also happens with setY()
Here is an example to reproduce the error:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this;
setContentView(R.layout.activity_main);
//Get edittext from layout file and set X and Y to the bottom
//left of screen
EditText edittext = findViewById(R.id.editText);
edittext.setX(0);
//Getting device frame to make sure its below keyboard, Not
//necessary can just guess any value
edittext.setY(getDeviceFrame(context).getHeight() / 1.2f);
}
With a layout of :
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
And a manifest of :
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
Resulting in a layout :
Edittext after programmatic setX and setY
And after clicking on editext we get :
Hidden editext
Which you can see is hidden by the keyboard.
UPDATE :
I have tried to use the different variations to moving the EditText with still no avail.
Ex.
edittext.animate().x(200);
UPDATE 2:
I have yet to solve this issue. I have been getting closer as I have done some test and it appears that using layout params to control the positioning of the view works to an extent.
Ex.
ConstraintLayout.LayoutParams layoutParams =
(ConstraintLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = 200; //Your X Coordinate
layoutParams.topMargin = 200; //Your Y Coordinate
view.setLayoutParams(layoutParams);
By some extent, I mean I have messed around with the functionality of these methods in a test library and it seems to work doing the above from onCreate(). However, in my app, I am doing the call in a callback. SetX works fine without me having to set it in a 'post' thread or 'runOnMainUI' thread. So I am not sure why this is not working.
Okay, so I figured out how to solve the issue. Using the answer from my "Update 2" above I was able to get the solution with a little tweaking.
This code:
ConstraintLayout.LayoutParams layoutParams =
(ConstraintLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = 200; //Your X Coordinate
layoutParams.topMargin = 200; //Your Y Coordinate
view.setLayoutParams(layoutParams);
(You can use whatever Layout you are using instead of Constraint)
Is an alternative to setX() and setY() and allows adjustPan to work. However, getting the views x or y positioning with :
view.getX();
view.getY();
Will not return the visual positioning of the view. You need to get the view's position by getting the layout params associated with the view and getting its margins.
Now what I came to realize and is key for the Constraint Layout is that you MUST have the constraint defined in xml or via code.
Ex.
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
Having this allows the margins to have a reference to what it was extended from, without this, the positioning of the view with not move.
Replace setX and setY with leftMargin and topMargin for pop up positioning, and it will work as intended on API greater than 23.
I can't explain why but the problem did not exist on API 23.
as suggested, here is a sample of the code used to move popup layout by drag on button and keep adjust pan working.
final View myNote = getActivity().getLayoutInflater().inflate(R.layout.viewer_annotation_layout, null);
final RelativeLayout.LayoutParams noteLayoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
myNote.setLayoutParams(noteLayoutParams);
mainScreenLayout.addView(myNote, noteLayoutParams);
btnmove.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//addednotedX = myNote.getX() - event.getRawX();
//addednotedY = myNote.getY() - event.getRawY();
addednotedX = noteLayoutParams.leftMargin - event.getRawX();
addednotedY = noteLayoutParams.topMargin - event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int newposx = event.getRawX() + addednotedX;
int newposy = event.getRawY() + addednotedY;
/* this is move the Note layout but prevent adjsutpan working */
//myNote.setX(newposx);
//myNote.setY(newposy);
/* this is move the Note layout and keep adjsutpan working */
noteLayoutParams.leftMargin = newposx;
noteLayoutParams.topMargin = newposy;
myNote.setLayoutParams(noteLayoutParams);
break;
default:
return false;
}
return false;
}
});
I have a recyclerview in which there is an edit text where it will hidden and opens up on click of some button.
Please refer to screen 1.
On click of reject or accept the view is displayed:
Now, my recyclerview is expanding downwards but I want it to expand upward like this:
By default, it expands downward:
I want it to expand upward like this:
Similarly, when keyboard opens up I want the whole recyclerview cell just above the keyboard like this.
By default, it does not move upward:
I want something like this:
What should be the approach for this functionality?
Do I have to do the calculations of cell height, keyboard height or is there some other method for this.
Please attach related links.
Thanks
I wrote this for my chat class you can rewrite this code snippet according to yours.
private boolean keyboardShown(View rootView) {
final int softKeyboardHeight = 100;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
int heightDiff = rootView.getBottom() - r.bottom;
return heightDiff > softKeyboardHeight * dm.density;
}
messageEditText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (keyboardShown(messageEditText.getRootView())) {
Log.d("keyboard", "keyboard UP");
if (keyboardUp == false) {
if (results.size() > 0)
chatList.smoothScrollToPosition(results.size()+1);
keyboardUp = true;
}
} else {
Log.d("keyboard", "keyboard Down");
keyboardUp = false;
}
}
});
Let's divide the question into two, scroll down the RecyclerView and show the cell when the keyboard is shown.
just use the scrollToPosition(index) with to the cell index
you can just change the android:windowSoftInputMode to be adjustSize in the activity:
<activity
android:windowSoftInputMode="adjustResize"
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
or just in the fragment:
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
Update : Problem is solved.
WARNING : This question Does not have the answer AT ALL which is mentioned above.
The problem is, there should be WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY instead of TYPE_PHONE.
If you are having the same problem I would suggest to ask for permission in java class as shown in the link mentioned by 0X0nosugar in the comments.
I am running a service which creates a transparent floating window with some information and user can drag and drop it anywhere.
So far, after adding all views, my service Crashed and I got this error.
java.lang.RuntimeException:
Unable to create service afm.dragger.Dragger:
android.view.WindowManager$BadTokenException:
Unable to add window android.view.ViewRootImpl$W#ba63d06 -- permission denied for window type 2002
My minSdkVersion is 21 and targetSdkVersion is 27 and I have sdk 27 on my phone.
Here is my AndroidManifest:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Dragger"/>
</application>
Here is my Dragger.class:
#Override
public void onCreate() {
super.onCreate();
WindowManager manager = (WindowManager) getSystemService(WINDOW_SERVICE);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setBackgroundColor(Color.parseColor("#33FFFFFF"));
LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
linearLayout.setLayoutParams(linearParams);
Button button = new Button(this);
button.setBackground(getResources().getDrawable(R.drawable.button_shape));
button.setTextColor(getResources().getColor(android.R.color.white));
button.setTextSize(14);
button.setText("Stop Service");
ViewGroup.LayoutParams buttonParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
button.setLayoutParams(buttonParams);
linearLayout.addView(button, buttonParams);
final WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams(350, 250, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
windowParams.x = 0;
windowParams.y = 0;
windowParams.gravity = Gravity.START|Gravity.TOP;
manager.addView(linearLayout, windowParams);
linearLayout.setOnTouchListener(new View.OnTouchListener() {
private WindowManager.LayoutParams updatedParams = windowParams;
int x, y;
float touchedX, touchedY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = updatedParams.x;
y = updatedParams.y;
touchedX = event.getRawX();
touchedY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
updatedParams.x = (int) (x + (event.getRawX() - touchedX));
updatedParams.y = (int) (y + (event.getRawY() - touchedY));
manager.updateViewLayout(linearLayout, updatedParams);
default:
break;
}
return false;
}
});
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
manager.removeView(linearLayout);
stopSelf();
}
});
}
And finally, here is my MainActivity:
start.setOnClickListener is set to :
startService(new Intent(MainActivity.this, Dragger.class));
This is a very old problem, but after making so much effort, I still cannot figure out a possible solution.
The Aim
Actually quite simple. I would like to develop some full screen apps (e.g. games), where the status bar does not expand, however the user touches the screen.
The app should be able to work on Android 5.0, sdk 21 (Lollipop).
What I have done so far
I find this post, which describes how to cover the status bar with a CustomView that absorbs touch events, but it doesn't work for me ...
I even changed the size of the CustomView so that it covers the whole screen, but it still doesn't work, and it cannot even cover my button.
The codes I use are attached below. When run on the device, both the button and the status bar work - nothing is blocked.
Am I missing something?
Codes
In AndroidManifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="#android:style/Theme.Holo.NoActionBar.Fullscreen">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
In activity_main.xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="#+id/text_show" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click"
android:id="#+id/button_click"
android:onClick="changeText"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
In MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager manager = ((WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources()
.getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
CustomViewGroup view = new CustomViewGroup(this);
manager.addView(view, localLayoutParams);
setContentView(R.layout.activity_main);
}
boolean key = false;
public void changeText(View view) {
TextView tv = (TextView) findViewById(R.id.text_show);
if (key) {
tv.setText("hahaha");
} else {
tv.setText("eroero");
}
key = ! key;
}
In CustomViewGroup (copied from the above link):
public class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("CustomViewGroup", "**********Intercepted");
return true;
}
}
Finally the best I can do is to use the Task Lock feature introduced in Android 5.0.
Reference: startLockTask.
The issues of this method:
Unless you have a helper app that grant lock task permission to your full screen app, the user will be asked if (s)he allows the lock.
Once locked, the user can still use physical buttons (home, back, volumn, etc.).
Since my intention is not to have a kiosk mode, I don't really care about point 2.
Point 1 is also acceptable, but a bit annoying since it will (and should) be asked every time onResume is called.
This can be solved by setting a service app as device owner, which grants permission to apps that demand it. But the user has to do this him(her)self (c.f. this answer), which is reasonable, otherwise an app would be able to totally kidnap your device.
I have one activity, ExampleActivity
<activity android:name="com.android.ExampleActivity"
android:label="#string/app_name"
android:allowEmbedded="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
With this define in manifiest, round layout is detect without problems.
But with this manifiest, the SAME activity, the SAME code not works.
<activity android:name="com.android.ExampleActivity"
android:label="#string/app_name"
android:allowEmbedded="true">
<meta-data android:name="com.google.android.clockwork.home.preview" android:resource="#drawable/example_watch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.google.android.clockwork.home.category.HOME_BACKGROUND" />
</intent-filter>
</activity>
For detect round layout, moto 360 device, using onApplyWindowInsets or onReadyForContent, but the same problem.
Any idea because when i used this category, com.google.android.clockwork.home.category.HOME_BACKGROUND, not works ?
Thanks
With the new SDK, you need to do it like it's shown on Android Wear for Developers
private class Engine extends CanvasWatchFaceService.Engine {
boolean mIsRound;
int mChinSize;
#Override
public void onApplyWindowInsets(WindowInsets insets) {
super.onApplyWindowInsets(insets);
mIsRound = insets.isRound();
mChinSize = insets.getSystemWindowInsetBottom();
}
...
}
Here, as you can see, you may also get a value of a bottom screen gap (ex. Moto 360).
Until the new andrioid smartwatch SDK is realeased, you can't use setOnApplyWindowInsetsListener / onApplyWindowInsets on custom watch face. This functionality only works on smartwatch app's (without add in manifest).
To know if the clock face is round, you can use:
public static boolean heightSameAsWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
return height == width;
}
private void checkIfWatchIsRound() {
if (heightSameAsWidth(getApplicationContext())) {
isRound = false;
} else {
isRound = true;
}
}