mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 385f516d63 | |||
| 6c89845cf9 | |||
| ee73ccf6ef | |||
| c9b6bb15e6 | |||
| e810f27ac2 | |||
| eeee1637c3 | |||
| 521adebda2 | |||
| b759203e93 |
@@ -360,6 +360,7 @@
|
||||
<activity android:name=".WebxdcActivity"
|
||||
android:label=""
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode">
|
||||
</activity>
|
||||
|
||||
|
||||
@@ -891,7 +891,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||
|
||||
@Override
|
||||
public void onReactionClicked(DcMsg messageRecord) {
|
||||
ReactionsDetailsFragment dialog = ReactionsDetailsFragment.newInstance(messageRecord.getId());
|
||||
ReactionsDetailsFragment dialog = new ReactionsDetailsFragment(messageRecord.getId());
|
||||
dialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
|
||||
findViewById(R.id.status_bar_background).setBackgroundResource(R.drawable.search_toolbar_shadow);
|
||||
} else {
|
||||
// add padding to avoid content hidden behind system bars
|
||||
ViewUtil.applyWindowInsets(findViewById(R.id.content_container));
|
||||
// Use applyWindowInsetsExcludingIme to respect navigation bars but not keyboard
|
||||
ViewUtil.applyWindowInsetsExcludingIme(findViewById(R.id.content_container));
|
||||
}
|
||||
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
+5
-25
@@ -47,40 +47,20 @@ import chat.delta.rpc.RpcException;
|
||||
public class AccountSelectionListFragment extends DialogFragment implements DcEventCenter.DcEventDelegate
|
||||
{
|
||||
private static final String TAG = AccountSelectionListFragment.class.getSimpleName();
|
||||
private static final String ARG_SELECT_ONLY = "select_only";
|
||||
|
||||
private ConversationListActivity activity;
|
||||
private final ConversationListActivity activity;
|
||||
private RecyclerView recyclerView;
|
||||
private AccountSelectionListAdapter adapter;
|
||||
private boolean selectOnly;
|
||||
private final boolean selectOnly;
|
||||
|
||||
public AccountSelectionListFragment() {
|
||||
public AccountSelectionListFragment(ConversationListActivity activity, boolean selectOnly) {
|
||||
super();
|
||||
}
|
||||
|
||||
public static AccountSelectionListFragment newInstance(boolean selectOnly) {
|
||||
AccountSelectionListFragment fragment = new AccountSelectionListFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(ARG_SELECT_ONLY, selectOnly);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
this.activity = activity;
|
||||
this.selectOnly = selectOnly;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Retrieve selectOnly from arguments (defaults to false if not present)
|
||||
if (getArguments() != null) {
|
||||
selectOnly = getArguments().getBoolean(ARG_SELECT_ONLY, false);
|
||||
}
|
||||
|
||||
// Get the activity from the context - must be ConversationListActivity
|
||||
if (getActivity() instanceof ConversationListActivity) {
|
||||
activity = (ConversationListActivity) getActivity();
|
||||
} else {
|
||||
throw new IllegalStateException("AccountSelectionListFragment must be attached to ConversationListActivity");
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.switch_account)
|
||||
.setNegativeButton(R.string.cancel, null);
|
||||
|
||||
@@ -138,7 +138,7 @@ public class AccountManager {
|
||||
// ui
|
||||
|
||||
public void showSwitchAccountMenu(ConversationListActivity activity, boolean selectOnly) {
|
||||
AccountSelectionListFragment dialog = AccountSelectionListFragment.newInstance(selectOnly);
|
||||
AccountSelectionListFragment dialog = new AccountSelectionListFragment(activity, selectOnly);
|
||||
dialog.show(((FragmentActivity) activity).getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ public class QrActivity extends BaseActionBarActivity implements View.OnClickLis
|
||||
|
||||
scanRelay = getIntent().getBooleanExtra(EXTRA_SCAN_RELAY, false);
|
||||
|
||||
qrShowFragment = new QrShowFragment();
|
||||
qrShowFragment = new QrShowFragment(this);
|
||||
tabLayout = ViewUtil.findById(this, R.id.tab_layout);
|
||||
viewPager = ViewUtil.findById(this, R.id.pager);
|
||||
ProfilePagerAdapter adapter = new ProfilePagerAdapter(this, getSupportFragmentManager());
|
||||
|
||||
@@ -54,8 +54,15 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
|
||||
private DcContext dcContext;
|
||||
|
||||
private View.OnClickListener scanClicklistener;
|
||||
|
||||
public QrShowFragment() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public QrShowFragment(View.OnClickListener scanClicklistener) {
|
||||
super();
|
||||
this.scanClicklistener = scanClicklistener;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,14 +79,9 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
dcContext = DcHelper.getContext(getActivity());
|
||||
dcEventCenter = DcHelper.getEventCenter(getActivity());
|
||||
|
||||
// First try to get chatId from fragment arguments, then fall back to activity intent
|
||||
if (getArguments() != null && getArguments().containsKey(CHAT_ID)) {
|
||||
chatId = getArguments().getInt(CHAT_ID);
|
||||
} else {
|
||||
Bundle extras = getActivity().getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
chatId = extras.getInt(CHAT_ID);
|
||||
}
|
||||
Bundle extras = getActivity().getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
chatId = extras.getInt(CHAT_ID);
|
||||
}
|
||||
|
||||
dcEventCenter.addObserver(DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS, this);
|
||||
@@ -106,16 +108,9 @@ public class QrShowFragment extends Fragment implements DcEventCenter.DcEventDel
|
||||
|
||||
view.findViewById(R.id.share_link_button).setOnClickListener((v) -> showInviteLinkDialog());
|
||||
Button scanBtn = view.findViewById(R.id.scan_qr_button);
|
||||
|
||||
// Check if the activity implements View.OnClickListener to provide scan functionality
|
||||
View.OnClickListener scanClickListener = null;
|
||||
if (getActivity() instanceof View.OnClickListener) {
|
||||
scanClickListener = (View.OnClickListener) getActivity();
|
||||
}
|
||||
|
||||
if (scanClickListener != null) {
|
||||
if (scanClicklistener != null) {
|
||||
scanBtn.setVisibility(View.VISIBLE);
|
||||
scanBtn.setOnClickListener(scanClickListener);
|
||||
scanBtn.setOnClickListener(scanClicklistener);
|
||||
} else {
|
||||
scanBtn.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@@ -36,32 +36,19 @@ import chat.delta.rpc.types.Reactions;
|
||||
|
||||
public class ReactionsDetailsFragment extends DialogFragment implements DcEventCenter.DcEventDelegate {
|
||||
private static final String TAG = ReactionsDetailsFragment.class.getSimpleName();
|
||||
private static final String ARG_MSG_ID = "msg_id";
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private ReactionRecipientsAdapter adapter;
|
||||
private int msgId;
|
||||
private final int msgId;
|
||||
|
||||
public ReactionsDetailsFragment() {
|
||||
public ReactionsDetailsFragment(int msgId) {
|
||||
super();
|
||||
}
|
||||
|
||||
public static ReactionsDetailsFragment newInstance(int msgId) {
|
||||
ReactionsDetailsFragment fragment = new ReactionsDetailsFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_MSG_ID, msgId);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Retrieve msgId from arguments
|
||||
if (getArguments() != null) {
|
||||
msgId = getArguments().getInt(ARG_MSG_ID, 0);
|
||||
}
|
||||
|
||||
adapter = new ReactionRecipientsAdapter(requireActivity(), GlideApp.with(requireActivity()), new ListClickListener());
|
||||
|
||||
LayoutInflater inflater = requireActivity().getLayoutInflater();
|
||||
|
||||
@@ -316,6 +316,23 @@ public class ViewUtil {
|
||||
return Insets.max(systemBars, displayCutout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get combined insets from status bar, navigation bar and display cutout areas,
|
||||
* excluding the IME (soft keyboard).
|
||||
*
|
||||
* @param windowInsets The window insets to extract from
|
||||
* @return Combined insets excluding IME
|
||||
*/
|
||||
private static Insets getCombinedInsetsExcludingIme(@NonNull WindowInsetsCompat windowInsets) {
|
||||
Insets systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
Insets displayCutout = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout());
|
||||
|
||||
// Combine systemBars (which excludes IME) with displayCutout using max to handle notches/cutouts
|
||||
Insets combined = Insets.max(systemBars, displayCutout);
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply window insets to a view by adding margin to avoid drawing it behind system bars.
|
||||
* Convenience method that applies insets to all sides.
|
||||
@@ -451,6 +468,67 @@ public class ViewUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply window insets to a view by adding padding to avoid drawing elements behind system bars,
|
||||
* excluding IME (soft keyboard) insets. This is useful for views that should respect navigation
|
||||
* bars but handle keyboard resizing through the window's soft input mode.
|
||||
*
|
||||
* This method stores the original padding values in view tags to ensure that
|
||||
* padding doesn't accumulate on multiple inset applications.
|
||||
*
|
||||
* Note: This feature is only enabled on API 30+ (Android 11+) where WindowInsets APIs
|
||||
* work correctly. On older API levels, the method returns early and the view will use
|
||||
* default system bar handling (content may be drawn behind system bars).
|
||||
*
|
||||
* @param view The view to apply insets to
|
||||
*/
|
||||
public static void applyWindowInsetsExcludingIme(@NonNull View view) {
|
||||
// Only enable on API 30+ where WindowInsets APIs work correctly
|
||||
if (!isEdgeToEdgeSupported()) return;
|
||||
|
||||
// Store the original padding as a tag only if not already stored
|
||||
// This prevents losing the true original padding on subsequent calls
|
||||
if (view.getTag(R.id.tag_window_insets_padding_left) == null) {
|
||||
view.setTag(R.id.tag_window_insets_padding_left, view.getPaddingLeft());
|
||||
view.setTag(R.id.tag_window_insets_padding_top, view.getPaddingTop());
|
||||
view.setTag(R.id.tag_window_insets_padding_right, view.getPaddingRight());
|
||||
view.setTag(R.id.tag_window_insets_padding_bottom, view.getPaddingBottom());
|
||||
}
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
|
||||
Insets insets = getCombinedInsetsExcludingIme(windowInsets);
|
||||
|
||||
// Retrieve the original padding values from tags with null checks
|
||||
Integer leftTag = (Integer) v.getTag(R.id.tag_window_insets_padding_left);
|
||||
Integer topTag = (Integer) v.getTag(R.id.tag_window_insets_padding_top);
|
||||
Integer rightTag = (Integer) v.getTag(R.id.tag_window_insets_padding_right);
|
||||
Integer bottomTag = (Integer) v.getTag(R.id.tag_window_insets_padding_bottom);
|
||||
int basePaddingLeft = leftTag != null ? leftTag : 0;
|
||||
int basePaddingTop = topTag != null ? topTag : 0;
|
||||
int basePaddingRight = rightTag != null ? rightTag : 0;
|
||||
int basePaddingBottom = bottomTag != null ? bottomTag : 0;
|
||||
|
||||
v.setPadding(
|
||||
basePaddingLeft + insets.left,
|
||||
basePaddingTop + insets.top,
|
||||
basePaddingRight + insets.right,
|
||||
basePaddingBottom + insets.bottom
|
||||
);
|
||||
|
||||
// Consume IME insets to prevent them from affecting child views (like WebView)
|
||||
// This stops the double-padding issue where both window resize and inset padding occur
|
||||
WindowInsetsCompat withoutIme = new WindowInsetsCompat.Builder(windowInsets)
|
||||
.setInsets(WindowInsetsCompat.Type.ime(), Insets.NONE)
|
||||
.build();
|
||||
return withoutIme;
|
||||
});
|
||||
|
||||
// Request the initial insets to be dispatched if the view is attached
|
||||
if (view.isAttachedToWindow()) {
|
||||
ViewCompat.requestApplyInsets(view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the top status bar inset as the height of a view.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user