Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf0467b5 authored by Mark Harman's avatar Mark Harman
Browse files

Changes in preparation for Android 15 edge-to-edge mode (for now disabled).

parent f44ec7d8
Loading
Loading
Loading
Loading
+68 −9
Original line number Diff line number Diff line
@@ -4867,11 +4867,16 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        setToDefault();
        boolean edge_to_edge_mode = mActivity.getEdgeToEdgeMode();
        Thread.sleep(1000);
        assertEquals(0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        assertFalse(mActivity.test_set_show_under_navigation);
        assertEquals(0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        int initial_navigation_gap = mActivity.getMainUI().test_navigation_gap;
        if( !edge_to_edge_mode ) {
            assertEquals(0, mActivity.getMainUI().test_navigation_gap);
        }
        // test changing resolution
        MainActivity.test_preview_want_no_limits_value = true;
@@ -4886,8 +4891,16 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        Log.d(TAG, "supports_hide_navigation: " + supports_hide_navigation);
        assertEquals(supports_no_limits ? WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS : 0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        if( edge_to_edge_mode ) {
            assertFalse(mActivity.test_set_show_under_navigation);
        }
        else {
            assertTrue(mActivity.test_set_show_under_navigation);
        }
        assertEquals(supports_hide_navigation ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        if( edge_to_edge_mode ) {
            assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
        }
        assertEquals(mActivity.getNavigationGap(), mActivity.getMainUI().test_navigation_gap);
        MainActivity.test_preview_want_no_limits_value = false;
        updateForSettings();
@@ -4895,7 +4908,12 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        assertEquals(0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        assertFalse(mActivity.test_set_show_under_navigation);
        assertEquals(0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        if( edge_to_edge_mode ) {
            assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
        }
        else {
            assertEquals(0, mActivity.getMainUI().test_navigation_gap);
        }
        if( mPreview.getCameraControllerManager().getNumberOfCameras() > 1 ) {
            // test switching camera
@@ -4903,8 +4921,16 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
            switchToCamera(1);
            Thread.sleep(1000);
            assertEquals(supports_no_limits ? WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS : 0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
            if( edge_to_edge_mode ) {
                assertFalse(mActivity.test_set_show_under_navigation);
            }
            else {
                assertTrue(mActivity.test_set_show_under_navigation);
            }
            assertEquals(supports_hide_navigation ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
            if( edge_to_edge_mode ) {
                assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
            }
            assertEquals(mActivity.getNavigationGap(), mActivity.getMainUI().test_navigation_gap);
            MainActivity.test_preview_want_no_limits_value = false;
            switchToCamera(0);
@@ -4912,8 +4938,13 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
            assertEquals(0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
            assertFalse(mActivity.test_set_show_under_navigation);
            assertEquals(0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
            if( edge_to_edge_mode ) {
                assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
            }
            else {
                assertEquals(0, mActivity.getMainUI().test_navigation_gap);
            }
        }
        // test switching to video and back
        View switchVideoButton = mActivity.findViewById(net.sourceforge.opencamera.R.id.switch_video);
@@ -4923,8 +4954,16 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        assertTrue(mPreview.isVideo());
        Thread.sleep(1000);
        assertEquals(supports_no_limits ? WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS : 0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        if( edge_to_edge_mode ) {
            assertFalse(mActivity.test_set_show_under_navigation);
        }
        else {
            assertTrue(mActivity.test_set_show_under_navigation);
        }
        assertEquals(supports_hide_navigation ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        if( edge_to_edge_mode ) {
            assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
        }
        assertEquals(mActivity.getNavigationGap(), mActivity.getMainUI().test_navigation_gap);
        MainActivity.test_preview_want_no_limits_value = false;
        clickView(switchVideoButton);
@@ -4934,15 +4973,28 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        assertEquals(0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        assertFalse(mActivity.test_set_show_under_navigation);
        assertEquals(0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        if( edge_to_edge_mode ) {
            assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
        }
        else {
            assertEquals(0, mActivity.getMainUI().test_navigation_gap);
        }
        // test after restart
        MainActivity.test_preview_want_no_limits_value = true;
        restart();
        Thread.sleep(1000);
        assertEquals(supports_no_limits ? WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS : 0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        if( edge_to_edge_mode ) {
            assertFalse(mActivity.test_set_show_under_navigation);
        }
        else {
            assertTrue(mActivity.test_set_show_under_navigation);
        }
        assertEquals(supports_hide_navigation ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        if( edge_to_edge_mode ) {
            assertEquals(initial_navigation_gap, mActivity.getMainUI().test_navigation_gap);
        }
        assertEquals(mActivity.getNavigationGap(), mActivity.getMainUI().test_navigation_gap);
    }
@@ -4965,6 +5017,8 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        setToDefault();
        boolean edge_to_edge_mode = mActivity.getEdgeToEdgeMode();
        Thread.sleep(1000);
        //boolean supports_no_limits = mActivity.getNavigationGap() != 0;
        final boolean supports_no_limits = false;
@@ -4977,7 +5031,12 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
        Log.d(TAG, "check FLAG_LAYOUT_NO_LIMITS");
        Log.d(TAG, "test_navigation_gap: " + mActivity.getMainUI().test_navigation_gap);
        assertEquals(supports_no_limits ? WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS : 0, mActivity.getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        if( edge_to_edge_mode ) {
            assertFalse(mActivity.test_set_show_under_navigation);
        }
        else {
            assertTrue(mActivity.test_set_show_under_navigation);
        }
        assertEquals(supports_hide_navigation ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0, mActivity.getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        assertEquals(mActivity.getNavigationGap(), mActivity.getMainUI().test_navigation_gap);
    }
+77 −15
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PorterDuff;
@@ -75,6 +76,8 @@ import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.exifinterface.media.ExifInterface;

import android.text.Html;
@@ -156,6 +159,8 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen

    //private boolean ui_placement_right = true;

    private boolean edge_to_edge_mode = false; // whether running always in edge-to-edge mode
    //private boolean edge_to_edge_mode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM; // whether running always in edge-to-edge mode
    private boolean want_no_limits; // whether we want to run with FLAG_LAYOUT_NO_LIMITS
    private boolean set_window_insets_listener; // whether we've enabled a setOnApplyWindowInsetsListener()
    private int navigation_gap;
@@ -2950,6 +2955,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
        this.enableScreenLockOnBackPressedCallback(false);

        Bundle bundle = new Bundle();
        bundle.putBoolean("edge_to_edge_mode", edge_to_edge_mode);
        bundle.putInt("cameraId", this.preview.getCameraId());
        bundle.putString("cameraIdSPhysical", this.applicationInterface.getCameraIdSPhysicalPref());
        bundle.putInt("nCameras", preview.getCameraControllerManager().getNumberOfCameras());
@@ -3706,6 +3712,13 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
        super.onBackPressed();
    }*/

    /** Returns whether we are always running in edge-to-edge mode. (If false, we may still sometimes
     *  run edge-to-edge.)
     */
    public boolean getEdgeToEdgeMode() {
        return this.edge_to_edge_mode;
    }

    /** Whether to allow the application to show under the navigation bar, or not.
     *  Arguably we could enable this all the time, but in practice we only enable for cases when
     *  want_no_limits==true and navigation_gap!=0 (if want_no_limits==false, there's no need to
@@ -3715,6 +3728,11 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
        if( MyDebug.LOG )
            Log.d(TAG, "showUnderNavigation: " + enable);

        if( edge_to_edge_mode ) {
            // we are already always in edge-to-edge mode
            return;
        }

        {
            // We used to use window flag FLAG_LAYOUT_NO_LIMITS, but this didn't work properly on
            // Android 11 (didn't take effect until orientation changed or application paused/resumed).
@@ -3736,7 +3754,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
    }

    public int getNavigationGap() {
        return want_no_limits ? navigation_gap : 0;
        return (want_no_limits || edge_to_edge_mode) ? navigation_gap : 0;
    }

    /** The system is now such that we have entered or exited immersive mode. If visible is true,
@@ -3786,16 +3804,42 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
                private SystemOrientation last_system_orientation;
                @Override
                public @NonNull WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets windowInsets) {
                    int inset_left = windowInsets.getSystemWindowInsetLeft();
                    //int inset_top = windowInsets.getSystemWindowInsetTop();
                    int inset_right = windowInsets.getSystemWindowInsetRight();
                    int inset_bottom = windowInsets.getSystemWindowInsetBottom();
                    int inset_left;
                    //int inset_top;
                    int inset_right;
                    int inset_bottom;
                    if( edge_to_edge_mode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ) {
                        // take opportunity to use non-deprecated versions; also for edge_to_edge_mode==true, we need to use getInsetsIgnoringVisibility for
                        // immersive mode (since for edge_to_edge_mode==true, we are not using setSystemUiVisibility() / SYSTEM_UI_FLAG_LAYOUT_STABLE in setImmersiveMode())
                        Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
                        inset_left = insets.left;
                        //inset_top = insets.top;
                        inset_right = insets.right;
                        inset_bottom = insets.bottom;
                    }
                    else {
                        inset_left = windowInsets.getSystemWindowInsetLeft();
                        //inset_top = windowInsets.getSystemWindowInsetTop();
                        inset_right = windowInsets.getSystemWindowInsetRight();
                        inset_bottom = windowInsets.getSystemWindowInsetBottom();
                    }
                    if( MyDebug.LOG ) {
                        Log.d(TAG, "inset left: " + inset_left);
                        //Log.d(TAG, "inset top: " + inset_top);
                        Log.d(TAG, "inset right: " + inset_right);
                        Log.d(TAG, "inset bottom: " + inset_bottom);
                    }

                    if( edge_to_edge_mode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ) {
                        // easier to ensure the entire activity avoids display cutouts - for the preview, we still support
                        // it showing under the navigation bar
                        Insets insets = windowInsets.getInsets(WindowInsets.Type.displayCutout());
                        v.setPadding(insets.left, insets.top, insets.right, insets.bottom);

                        // also handle change of immersive mode (instead of using deprecated setOnSystemUiVisibilityChangeListener below
                        immersiveModeChanged( windowInsets.isVisible(WindowInsets.Type.navigationBars()) );
                    }

                    SystemOrientation system_orientation = getSystemOrientation();
                    int new_navigation_gap;
                    switch ( system_orientation ) {
@@ -3822,7 +3866,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen

                        if( MyDebug.LOG )
                            Log.d(TAG, "want_no_limits: " + want_no_limits);
                        if( want_no_limits ) {
                        if( want_no_limits || edge_to_edge_mode ) {
                            // If we want no_limits mode, then need to take care in case of device orientation
                            // in cases where that changes the navigation_gap:
                            // - Need to set showUnderNavigation() (in case navigation_gap when from zero to non-zero or vice versa).
@@ -3900,6 +3944,10 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
            });
        }

        if( edge_to_edge_mode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ) {
            // already handled by the setOnApplyWindowInsetsListener above
        }
        else {
            decorView.setOnSystemUiVisibilityChangeListener
                (new View.OnSystemUiVisibilityChangeListener() {
                    @Override
@@ -3925,6 +3973,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
                    }
                });
        }
    }

    public boolean usingKitKatImmersiveMode() {
        // whether we are using a Kit Kat style immersive mode (either hiding navigation bar, GUI, or everything)
@@ -3996,6 +4045,18 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
        if( MyDebug.LOG )
            Log.d(TAG, "enable_immersive?: " + enable_immersive);

        if( edge_to_edge_mode ) {
            // take opportunity to avoid deprecated setSystemUiVisibility
            WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
            int type = WindowInsetsCompat.Type.navigationBars(); // only show/hide navigation bars, as we run with system bars always hidden
            if( enable_immersive ) {
                windowInsetsController.hide(type);
            }
            else {
                windowInsetsController.show(type);
            }
        }
        else {
            // save whether we set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - since this flag might be enabled for showUnderNavigation(true), at least indirectly by setDecorFitsSystemWindows() on old versions of Android
            int saved_flags = getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
            if( MyDebug.LOG )
@@ -4007,6 +4068,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
                getWindow().getDecorView().setSystemUiVisibility(saved_flags);
            }
        }
    }

    /** Sets the brightness level for normal operation (when camera preview is visible).
     *  If force_max is true, this always forces maximum brightness; otherwise this depends on user preference.
@@ -5540,7 +5602,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen
            // device orientation (because application can e.g. be in landscape mode even if device
            // has switched to portrait)
        }
        else if( set_window_insets_listener ) {
        else if( set_window_insets_listener && !edge_to_edge_mode ) {
            Point display_size = new Point();
            applicationInterface.getDisplaySize(display_size);
            int display_width = Math.max(display_size.x, display_size.y);
+26 −0
Original line number Diff line number Diff line
@@ -47,6 +47,9 @@ import android.view.WindowMetrics;
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -70,6 +73,8 @@ import java.util.List;
public class MyPreferenceFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
    private static final String TAG = "MyPreferenceFragment";

    private boolean edge_to_edge_mode = false;

    private int cameraId;

    /* Any AlertDialogs we create should be added to dialogs, and removed when dismissed. Any dialogs still
@@ -91,6 +96,7 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared
        addPreferencesFromResource(R.xml.preferences);

        final Bundle bundle = getArguments();
        this.edge_to_edge_mode = bundle.getBoolean("edge_to_edge_mode");
        this.cameraId = bundle.getInt("cameraId");
        if( MyDebug.LOG )
            Log.d(TAG, "cameraId: " + cameraId);
@@ -751,6 +757,26 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared
        setupDependencies();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if( edge_to_edge_mode ) {
            handleEdgeToEdge(view);
        }
    }

    static void handleEdgeToEdge(View view) {
        ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
            //androidx.core.graphics.Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
            // don't need to avoid WindowInsetsCompat.Type.displayCutout(), as we already do this for the entire activity (see MainActivity's setOnApplyWindowInsetsListener)
            androidx.core.graphics.Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
            return WindowInsetsCompat.CONSUMED;
        });
        view.requestApplyInsets();
    }

    /** Adds a TextView to an AlertDialog builder, placing it inside a scrollview and adding appropriate padding.
     */
    private void addTextViewForAlertDialog(AlertDialog.Builder alertDialog, TextView textView) {
+15 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;

import java.util.HashSet;

@@ -15,6 +16,8 @@ import java.util.HashSet;
public class PreferenceSubScreen extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    private static final String TAG = "PreferenceSubScreen";

    private boolean edge_to_edge_mode = false;

    // see note for dialogs in MyPreferenceFragment
    protected final HashSet<AlertDialog> dialogs = new HashSet<>();

@@ -24,10 +27,22 @@ public class PreferenceSubScreen extends PreferenceFragment implements SharedPre
            Log.d(TAG, "onCreate");
        super.onCreate(savedInstanceState);

        final Bundle bundle = getArguments();
        this.edge_to_edge_mode = bundle.getBoolean("edge_to_edge_mode");

        if( MyDebug.LOG )
            Log.d(TAG, "onCreate done");
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if( edge_to_edge_mode ) {
            MyPreferenceFragment.handleEdgeToEdge(view);
        }
    }

    @Override
    public void onDestroy() {
        if( MyDebug.LOG )