Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public ArchiveConfirmationFragment(PackageUserAction dialogData) {
mDialogData = dialogData;
}

@Override
protected boolean shouldHandleConfirmationDpadKeys() {
return true;
}

@Override
public void onStart() {
super.onStart();
Expand Down
148 changes: 132 additions & 16 deletions app/src/main/java/io/i10nstaller/ui/fragments/BaseDialogFragment.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package io.i10nstaller.ui.fragments;

import android.app.AlertDialog;
import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -20,6 +19,9 @@ public class BaseDialogFragment extends DialogFragment {

public final InstallStage mDialogData;
InstallViewModel mViewModel;
private ViewTreeObserver mDialogViewTreeObserver;
private ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusChangeListener;
private ViewTreeObserver.OnTouchModeChangeListener mTouchModeChangeListener;

public BaseDialogFragment(InstallStage dialogData) {
mDialogData = dialogData;
Expand All @@ -36,7 +38,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
public void onStart() {
super.onStart();
if (getDialog() instanceof AlertDialog alertDialog) {
boolean isTvDevice = isTvDevice();
for (int which : new int[] {
AlertDialog.BUTTON_POSITIVE,
AlertDialog.BUTTON_NEGATIVE,
Expand All @@ -45,37 +46,152 @@ public void onStart() {
var button = alertDialog.getButton(which);
if (button != null) {
button.setFocusable(true);
button.setFocusableInTouchMode(
isTvDevice && which == AlertDialog.BUTTON_POSITIVE);
button.setFocusableInTouchMode(false);
button.setClickable(true);
}
}
if (shouldHandleConfirmationDpadKeys()) {
configureConfirmationDialog(alertDialog);
}
}
}

private void configureConfirmationDialog(AlertDialog dialog) {
var positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
var negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
var neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
if (positiveButton == null) {
return;
}

positiveButton.setFocusable(true);
positiveButton.setFocusableInTouchMode(false);
if (negativeButton != null) {
negativeButton.setFocusableInTouchMode(false);
}
if (neutralButton != null) {
neutralButton.setFocusableInTouchMode(false);
}
positiveButton.post(() -> {
if (!positiveButton.isInTouchMode()) {
positiveButton.requestFocus();
}
});

var decorView = dialog.getWindow().getDecorView();
mDialogViewTreeObserver = decorView.getViewTreeObserver();
mWindowFocusChangeListener = hasFocus -> {
if (hasFocus) {
focusPositiveUnlessOtherButtonFocused(
positiveButton, negativeButton, neutralButton);
}
};
mTouchModeChangeListener = inTouchMode -> {
if (!inTouchMode) {
focusPositiveUnlessOtherButtonFocused(
positiveButton, negativeButton, neutralButton);
}
};
mDialogViewTreeObserver.addOnWindowFocusChangeListener(mWindowFocusChangeListener);
mDialogViewTreeObserver.addOnTouchModeChangeListener(mTouchModeChangeListener);

dialog.setOnKeyListener((ignored, keyCode, event) ->
handleConfirmationKey(
positiveButton, negativeButton, neutralButton, keyCode, event));
}

private void focusPositiveUnlessOtherButtonFocused(
View positiveButton, View negativeButton, View neutralButton) {
if (!positiveButton.isInTouchMode()
&& !hasFocus(negativeButton)
&& !hasFocus(neutralButton)) {
positiveButton.requestFocus();
}
}

private boolean hasFocus(View button) {
return button != null && button.hasFocus();
}

private boolean handleConfirmationKey(
View positiveButton, View negativeButton, View neutralButton,
int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
|| keyCode == KeyEvent.KEYCODE_ENTER
|| keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) {
if (hasFocus(negativeButton) || hasFocus(neutralButton)) {
return false;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
positiveButton.setPressed(true);
return true;
}
if (event.getAction() == KeyEvent.ACTION_UP) {
positiveButton.setPressed(false);
if (!event.isCanceled() && positiveButton.isEnabled()) {
positiveButton.performClick();
}
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (negativeButton != null && negativeButton.isEnabled()) {
negativeButton.requestFocus();
}
return true;
}
}
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (positiveButton.isEnabled()) {
positiveButton.requestFocus();
}
return true;
}
}
return false;
}

private boolean isTvDevice() {
var context = requireContext();
var uiModeManager =
(UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE);
return (uiModeManager != null
&& uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION)
|| !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
protected boolean shouldHandleConfirmationDpadKeys() {
return false;
}

protected void focusPositiveButtonForDpad() {
if (getDialog() instanceof AlertDialog alertDialog) {
var positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
if (positiveButton != null) {
boolean isTvDevice = isTvDevice();
positiveButton.post(() -> {
if (isTvDevice || !positiveButton.isInTouchMode()) {
if (!positiveButton.isInTouchMode()) {
positiveButton.requestFocus();
}
});
}
}
}

@Override
public void onStop() {
removeDialogFocusListeners();
super.onStop();
}

private void removeDialogFocusListeners() {
if (mDialogViewTreeObserver != null && mDialogViewTreeObserver.isAlive()) {
if (mWindowFocusChangeListener != null) {
mDialogViewTreeObserver.removeOnWindowFocusChangeListener(
mWindowFocusChangeListener);
}
if (mTouchModeChangeListener != null) {
mDialogViewTreeObserver.removeOnTouchModeChangeListener(
mTouchModeChangeListener);
}
}
mDialogViewTreeObserver = null;
mWindowFocusChangeListener = null;
mTouchModeChangeListener = null;
}

@Override
public void onCancel(@NonNull DialogInterface dialog) {
super.onCancel(dialog);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public InstallConfirmationFragment(InstallUserAction dialogData) {
mDialogData = dialogData;
}

@Override
protected boolean shouldHandleConfirmationDpadKeys() {
return true;
}

@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Expand Down
Loading