mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
271 lines
7.7 KiB
Java
271 lines
7.7 KiB
Java
package org.thoughtcrime.securesms.components;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.content.Context;
|
|
import android.content.SharedPreferences;
|
|
import android.graphics.Rect;
|
|
import android.os.Build;
|
|
import android.preference.PreferenceManager;
|
|
import android.support.annotation.Nullable;
|
|
import android.support.annotation.UiThread;
|
|
import android.util.AttributeSet;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.widget.RelativeLayout;
|
|
|
|
import org.briarproject.R;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.logging.Logger;
|
|
|
|
import static android.content.Context.WINDOW_SERVICE;
|
|
import static android.view.Surface.ROTATION_270;
|
|
import static android.view.Surface.ROTATION_90;
|
|
import static java.util.logging.Level.INFO;
|
|
import static java.util.logging.Level.WARNING;
|
|
|
|
/**
|
|
* RelativeLayout that, when a view container, will report back when it thinks
|
|
* a soft keyboard has been opened and what its height would be.
|
|
*/
|
|
@UiThread
|
|
public class KeyboardAwareRelativeLayout extends RelativeLayout {
|
|
|
|
private static final Logger LOG =
|
|
Logger.getLogger(KeyboardAwareRelativeLayout.class.getName());
|
|
|
|
private final Rect rect = new Rect();
|
|
private final Set<OnKeyboardHiddenListener> hiddenListeners =
|
|
new HashSet<>();
|
|
private final Set<OnKeyboardShownListener> shownListeners = new HashSet<>();
|
|
private final int minKeyboardSize;
|
|
private final int minCustomKeyboardSize;
|
|
private final int defaultCustomKeyboardSize;
|
|
private final int minCustomKeyboardTopMargin;
|
|
private final int statusBarHeight;
|
|
|
|
private int viewInset;
|
|
|
|
private boolean keyboardOpen = false;
|
|
private int rotation = -1;
|
|
|
|
public KeyboardAwareRelativeLayout(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public KeyboardAwareRelativeLayout(Context context,
|
|
@Nullable AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public KeyboardAwareRelativeLayout(Context context,
|
|
@Nullable AttributeSet attrs, int defStyle) {
|
|
super(context, attrs, defStyle);
|
|
final int statusBarRes = getResources()
|
|
.getIdentifier("status_bar_height", "dimen", "android");
|
|
minKeyboardSize =
|
|
getResources().getDimensionPixelSize(R.dimen.min_keyboard_size);
|
|
minCustomKeyboardSize = getResources()
|
|
.getDimensionPixelSize(R.dimen.min_custom_keyboard_size);
|
|
defaultCustomKeyboardSize = getResources()
|
|
.getDimensionPixelSize(R.dimen.default_custom_keyboard_size);
|
|
minCustomKeyboardTopMargin = getResources()
|
|
.getDimensionPixelSize(R.dimen.min_custom_keyboard_top_margin);
|
|
statusBarHeight = statusBarRes > 0 ?
|
|
getResources().getDimensionPixelSize(statusBarRes) : 0;
|
|
viewInset = getViewInset();
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
updateRotation();
|
|
updateKeyboardState();
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
|
|
private void updateRotation() {
|
|
int oldRotation = rotation;
|
|
rotation = getDeviceRotation();
|
|
if (oldRotation != rotation) {
|
|
LOG.info("Rotation changed");
|
|
onKeyboardClose();
|
|
}
|
|
}
|
|
|
|
private void updateKeyboardState() {
|
|
if (isLandscape()) {
|
|
if (keyboardOpen) onKeyboardClose();
|
|
return;
|
|
}
|
|
|
|
if (viewInset == 0 && Build.VERSION.SDK_INT >= 21)
|
|
viewInset = getViewInset();
|
|
int availableHeight =
|
|
getRootView().getHeight() - statusBarHeight - viewInset;
|
|
getWindowVisibleDisplayFrame(rect);
|
|
|
|
int keyboardHeight = availableHeight - (rect.bottom - rect.top);
|
|
|
|
if (keyboardHeight > minKeyboardSize) {
|
|
if (getKeyboardHeight() != keyboardHeight)
|
|
setKeyboardPortraitHeight(keyboardHeight);
|
|
if (!keyboardOpen) onKeyboardOpen(keyboardHeight);
|
|
} else if (keyboardOpen) {
|
|
onKeyboardClose();
|
|
}
|
|
}
|
|
|
|
@TargetApi(21)
|
|
private int getViewInset() {
|
|
try {
|
|
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
|
attachInfoField.setAccessible(true);
|
|
Object attachInfo = attachInfoField.get(this);
|
|
if (attachInfo != null) {
|
|
Field stableInsetsField =
|
|
attachInfo.getClass().getDeclaredField("mStableInsets");
|
|
stableInsetsField.setAccessible(true);
|
|
Rect insets = (Rect) stableInsetsField.get(attachInfo);
|
|
return insets.bottom;
|
|
}
|
|
} catch (NoSuchFieldException e) {
|
|
LOG.log(WARNING,
|
|
"field reflection error when measuring view inset", e);
|
|
} catch (IllegalAccessException e) {
|
|
LOG.log(WARNING,
|
|
"access reflection error when measuring view inset", e);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
protected void onKeyboardOpen(int keyboardHeight) {
|
|
if (LOG.isLoggable(INFO))
|
|
LOG.info("onKeyboardOpen(" + keyboardHeight + ")");
|
|
keyboardOpen = true;
|
|
|
|
notifyShownListeners();
|
|
}
|
|
|
|
protected void onKeyboardClose() {
|
|
LOG.info("onKeyboardClose()");
|
|
keyboardOpen = false;
|
|
notifyHiddenListeners();
|
|
}
|
|
|
|
public boolean isKeyboardOpen() {
|
|
return keyboardOpen;
|
|
}
|
|
|
|
public int getKeyboardHeight() {
|
|
return isLandscape() ? getKeyboardLandscapeHeight() :
|
|
getKeyboardPortraitHeight();
|
|
}
|
|
|
|
public boolean isLandscape() {
|
|
int rotation = getDeviceRotation();
|
|
return rotation == ROTATION_90 || rotation == ROTATION_270;
|
|
}
|
|
|
|
private int getDeviceRotation() {
|
|
WindowManager windowManager =
|
|
(WindowManager) getContext().getSystemService(WINDOW_SERVICE);
|
|
return windowManager.getDefaultDisplay().getRotation();
|
|
}
|
|
|
|
private int getKeyboardLandscapeHeight() {
|
|
return Math.max(getHeight(), getRootView().getHeight()) / 2;
|
|
}
|
|
|
|
private int getKeyboardPortraitHeight() {
|
|
SharedPreferences prefs =
|
|
PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
int keyboardHeight = prefs.getInt("keyboard_height_portrait",
|
|
defaultCustomKeyboardSize);
|
|
return clamp(keyboardHeight, minCustomKeyboardSize,
|
|
getRootView().getHeight() - minCustomKeyboardTopMargin);
|
|
}
|
|
|
|
private int clamp(int value, int min, int max) {
|
|
return Math.min(Math.max(value, min), max);
|
|
}
|
|
|
|
private void setKeyboardPortraitHeight(int height) {
|
|
SharedPreferences prefs =
|
|
PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
prefs.edit().putInt("keyboard_height_portrait", height).apply();
|
|
}
|
|
|
|
public void postOnKeyboardClose(final Runnable runnable) {
|
|
if (keyboardOpen) {
|
|
addOnKeyboardHiddenListener(new OnKeyboardHiddenListener() {
|
|
@Override
|
|
public void onKeyboardHidden() {
|
|
removeOnKeyboardHiddenListener(this);
|
|
runnable.run();
|
|
}
|
|
});
|
|
} else {
|
|
runnable.run();
|
|
}
|
|
}
|
|
|
|
public void postOnKeyboardOpen(final Runnable runnable) {
|
|
if (!keyboardOpen) {
|
|
addOnKeyboardShownListener(new OnKeyboardShownListener() {
|
|
@Override
|
|
public void onKeyboardShown() {
|
|
removeOnKeyboardShownListener(this);
|
|
runnable.run();
|
|
}
|
|
});
|
|
} else {
|
|
runnable.run();
|
|
}
|
|
}
|
|
|
|
public void addOnKeyboardHiddenListener(OnKeyboardHiddenListener listener) {
|
|
hiddenListeners.add(listener);
|
|
}
|
|
|
|
public void removeOnKeyboardHiddenListener(
|
|
OnKeyboardHiddenListener listener) {
|
|
hiddenListeners.remove(listener);
|
|
}
|
|
|
|
public void addOnKeyboardShownListener(OnKeyboardShownListener listener) {
|
|
shownListeners.add(listener);
|
|
}
|
|
|
|
public void removeOnKeyboardShownListener(
|
|
OnKeyboardShownListener listener) {
|
|
shownListeners.remove(listener);
|
|
}
|
|
|
|
private void notifyHiddenListeners() {
|
|
// Make a copy as listeners may remove themselves when called
|
|
Set<OnKeyboardHiddenListener> listeners =
|
|
new HashSet<>(hiddenListeners);
|
|
for (OnKeyboardHiddenListener listener : listeners) {
|
|
listener.onKeyboardHidden();
|
|
}
|
|
}
|
|
|
|
private void notifyShownListeners() {
|
|
// Make a copy as listeners may remove themselves when called
|
|
Set<OnKeyboardShownListener> listeners = new HashSet<>(shownListeners);
|
|
for (OnKeyboardShownListener listener : listeners) {
|
|
listener.onKeyboardShown();
|
|
}
|
|
}
|
|
|
|
public interface OnKeyboardHiddenListener {
|
|
void onKeyboardHidden();
|
|
}
|
|
|
|
public interface OnKeyboardShownListener {
|
|
void onKeyboardShown();
|
|
}
|
|
}
|