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

Commit a21468a7 authored by Shivangi Dubey's avatar Shivangi Dubey Committed by Android (Google) Code Review
Browse files

Merge "Add dependency SettingsLibDeviceStateRotationLock to WM" into main

parents d70786ae 4f92a142
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -13,7 +13,6 @@ android_library {

    srcs: [
        "src/**/*.java",
        "src/**/*.kt",
    ],

    min_sdk_version: "21",
+108 −0
Original line number Diff line number Diff line
@@ -14,94 +14,95 @@
 * limitations under the License.
 */

package com.android.settingslib.devicestate
package com.android.settingslib.devicestate;

import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
import android.util.Dumpable
import android.util.SparseIntArray
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Dumpable;
import android.util.SparseIntArray;

import java.util.List;

/**
 * Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
 *
 * Interface for managing {@link DEVICE_STATE_ROTATION_LOCK} setting.
 * <p>
 * It provides methods to register/unregister listeners for setting changes, update the setting for
 * specific device states, retrieve the setting value, and check if rotation is locked for specific
 * or all device states.
 */
interface DeviceStateAutoRotateSettingManager : Dumpable {
public interface DeviceStateAutoRotateSettingManager extends Dumpable {
    // TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.

    /** Listener for changes in device-state based auto rotate setting. */
    interface DeviceStateAutoRotateSettingListener {
        /** Called whenever the setting has changed. */
        fun onSettingsChanged()
        void onSettingsChanged();
    }

    /** Register listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
    fun registerListener(settingListener: DeviceStateAutoRotateSettingListener)
    /** Register listener for changes to {@link DEVICE_STATE_ROTATION_LOCK} setting. */
    void registerListener(@NonNull DeviceStateAutoRotateSettingListener settingListener);

    /** Unregister listener for changes to [DEVICE_STATE_ROTATION_LOCK] setting. */
    fun unregisterListener(settingListener: DeviceStateAutoRotateSettingListener)
    /** Unregister listener for changes to {@link DEVICE_STATE_ROTATION_LOCK} setting. */
    void unregisterListener(@NonNull DeviceStateAutoRotateSettingListener settingListener);

    /**
     * Write [deviceState]'s setting value as [autoRotate], for [DEVICE_STATE_ROTATION_LOCK]
     * setting.
     * Write {@code deviceState}'s setting value as {@code autoRotate}, for
     * {@link DEVICE_STATE_ROTATION_LOCK} setting.
     */
    fun updateSetting(deviceState: Int, autoRotate: Boolean)
    void updateSetting(int deviceState, boolean autoRotate);

    /**
     * Get [DEVICE_STATE_ROTATION_LOCK] setting value for [deviceState]. Returns null if string
     * value of [DEVICE_STATE_ROTATION_LOCK] is corrupted.
     *
     * Get {@link DEVICE_STATE_ROTATION_LOCK} setting value for {@code deviceState}. Returns null if
     * string value of {@link DEVICE_STATE_ROTATION_LOCK} is corrupted.
     * <p>
     * If the value is null, system_server will shortly reset the value of
     * [DEVICE_STATE_ROTATION_LOCK]. Clients can either subscribe to setting changes or query this
     * API again after a brief delay.
     * {@link DEVICE_STATE_ROTATION_LOCK}. Clients can either subscribe to setting changes or query
     * this API again after a brief delay.
     */
    fun getRotationLockSetting(deviceState: Int): Int?
    @Nullable
    Integer getRotationLockSetting(int deviceState);

    /**
     * Get [DEVICE_STATE_ROTATION_LOCK] setting value in form of integer to integer map. Returns
     * null if string value of [DEVICE_STATE_ROTATION_LOCK] is corrupted.
     *
     * Get {@link DEVICE_STATE_ROTATION_LOCK} setting value in form of integer to integer map.
     * Returns null if string value of {@link DEVICE_STATE_ROTATION_LOCK} is corrupted.
     * <p>
     * If the value is null, system_server will shortly reset the value of
     * [DEVICE_STATE_ROTATION_LOCK]. Clients can either subscribe to setting changes or query this
     * API again after a brief delay.
     * {@link DEVICE_STATE_ROTATION_LOCK}. Clients can either subscribe to setting changes or query
     * this API again after a brief delay.
     */
    fun getRotationLockSetting(): SparseIntArray?
    @Nullable
    SparseIntArray getRotationLockSetting();

    /**
     * Returns true if auto-rotate setting is OFF for [deviceState]. Returns null if string value
     * of [DEVICE_STATE_ROTATION_LOCK] is corrupted.
     *
     * Returns true if auto-rotate setting is OFF for {@code deviceState}. Returns null if string
     * value of {@link DEVICE_STATE_ROTATION_LOCK} is corrupted.
     * <p>
     * If the value is null, system_server will shortly reset the value of
     * [DEVICE_STATE_ROTATION_LOCK]. Clients can either subscribe to setting changes or query this
     * API again after a brief delay.
     * {@link DEVICE_STATE_ROTATION_LOCK}. Clients can either subscribe to setting changes or query
     * this API again after a brief delay.
     */
    fun isRotationLocked(deviceState: Int): Boolean?
    @Nullable
    Boolean isRotationLocked(int deviceState);

    /**
     * Returns true if the auto-rotate setting value for all device states is OFF. Returns null if
     * string value of [DEVICE_STATE_ROTATION_LOCK] is corrupted.
     *
     * string value of {@link DEVICE_STATE_ROTATION_LOCK} is corrupted.
     * <p>
     * If the value is null, system_server will shortly reset the value of
     * [DEVICE_STATE_ROTATION_LOCK]. Clients can either subscribe to setting changes or query this
     * API again after a brief delay.
     * {@link DEVICE_STATE_ROTATION_LOCK}. Clients can either subscribe to setting changes or query
     * this API again after a brief delay.
     */
    fun isRotationLockedForAllStates(): Boolean?
    @Nullable
    Boolean isRotationLockedForAllStates();

    /** Returns a list of device states and their respective auto rotate setting availability. */
    fun getSettableDeviceStates(): List<SettableDeviceState>
    @NonNull
    List<SettableDeviceState> getSettableDeviceStates();

    /**
     * Returns default value of [DEVICE_STATE_ROTATION_LOCK] setting from config, in form of integer
     * to integer map.
     * Returns default value of {@link DEVICE_STATE_ROTATION_LOCK} setting from config, in form of
     * integer to integer map.
     */
    fun getDefaultRotationLockSetting(): SparseIntArray
    @NonNull
    SparseIntArray getDefaultRotationLockSetting();
}

/** Represents a device state and whether it has an auto-rotation setting. */
data class SettableDeviceState(
    /** Returns the device state associated with this object. */
    val deviceState: Int,
    /** Returns whether there is an auto-rotation setting for this device state. */
    val isSettable: Boolean
)
+273 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settingslib.devicestate;

import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;

import android.annotation.NonNull;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseIntArray;

import com.android.internal.R;
import com.android.window.flags.Flags;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * Implementation of {@link DeviceStateAutoRotateSettingManager}. This implementation is a part of
 * refactoring, it should be used when
 * {@link Flags#FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR}
 * is enabled.
 */
public class DeviceStateAutoRotateSettingManagerImpl implements
        DeviceStateAutoRotateSettingManager {
    // TODO: b/397928958 rename the fields and apis from rotationLock to autoRotate.

    private static final String TAG = "DSAutoRotateMngr";
    private static final String SEPARATOR_REGEX = ":";

    private final Set<DeviceStateAutoRotateSettingListener> mSettingListeners = new HashSet<>();
    private final SparseIntArray mFallbackPostureMap = new SparseIntArray();
    private final SparseIntArray mDefaultDeviceStateAutoRotateSetting = new SparseIntArray();
    private final List<SettableDeviceState> mSettableDeviceState = new ArrayList<>();
    private final SecureSettings mSecureSettings;
    private final Handler mMainHandler;
    private final PosturesHelper mPosturesHelper;

    public DeviceStateAutoRotateSettingManagerImpl(
            Context context,
            Executor backgroundExecutor,
            SecureSettings secureSettings,
            Handler mainHandler,
            PosturesHelper posturesHelper
    ) {
        mSecureSettings = secureSettings;
        mMainHandler = mainHandler;
        mPosturesHelper = posturesHelper;

        loadAutoRotateDeviceStates(context);
        final ContentObserver contentObserver = new ContentObserver(mMainHandler) {
            @Override
            public void onChange(boolean selfChange) {
                notifyListeners();
            }
        };
        backgroundExecutor.execute(() ->
                mSecureSettings.registerContentObserver(
                        DEVICE_STATE_ROTATION_LOCK,
                        /* notifyForDescendants= */false,
                        contentObserver,
                        UserHandle.USER_CURRENT
                )
        );
    }

    @Override
    public void registerListener(@NonNull DeviceStateAutoRotateSettingListener settingListener) {
        mSettingListeners.add(settingListener);
    }

    @Override
    public void unregisterListener(@NonNull DeviceStateAutoRotateSettingListener settingListener) {
        if (!mSettingListeners.remove(settingListener)) {
            Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
        }
    }

    @Override
    @Settings.Secure.DeviceStateRotationLockSetting
    public Integer getRotationLockSetting(int deviceState) {
        final int devicePosture = mPosturesHelper.deviceStateToPosture(deviceState);
        final SparseIntArray deviceStateAutoRotateSetting = getRotationLockSetting();
        final Integer autoRotateSettingValue = extractSettingForDevicePosture(devicePosture,
                deviceStateAutoRotateSetting);

        // If the setting is ignored for this posture, check the fallback posture.
        if (autoRotateSettingValue != null
                && autoRotateSettingValue == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
            final int fallbackPosture = mFallbackPostureMap.get(devicePosture,
                    DEVICE_STATE_ROTATION_LOCK_IGNORED);
            return extractSettingForDevicePosture(fallbackPosture, deviceStateAutoRotateSetting);
        }

        return autoRotateSettingValue;
    }

    @Override
    public SparseIntArray getRotationLockSetting() {
        final String serializedSetting = mSecureSettings.getStringForUser(
                DEVICE_STATE_ROTATION_LOCK,
                UserHandle.USER_CURRENT);
        if (serializedSetting == null || serializedSetting.isEmpty()) return null;
        try {
            final String[] deserializedSettings = serializedSetting.split(SEPARATOR_REGEX);
            if (deserializedSettings.length % 2 != 0) {
                throw new IllegalStateException("Odd number of elements in the list");
            }
            final SparseIntArray deviceStateAutoRotateSetting = new SparseIntArray();
            for (int i = 0; i < deserializedSettings.length; i += 2) {
                final int key = Integer.parseInt(deserializedSettings[i]);
                final int value = Integer.parseInt(deserializedSettings[i + 1]);
                if (value < 0 || value > 2) {
                    throw new IllegalStateException(
                            "Invalid value in pair: key=" + deserializedSettings[i] + ", value="
                                    + deserializedSettings[i + 1]);
                }
                deviceStateAutoRotateSetting.put(key, value);
            }
            return deviceStateAutoRotateSetting;
        } catch (Exception e) {
            Log.w(TAG, "Invalid format in serializedSetting=" + serializedSetting, e);
            return null;
        }
    }

    @Override
    public Boolean isRotationLocked(int deviceState) {
        final Integer autoRotateValue = getRotationLockSetting(deviceState);
        return autoRotateValue == null ? null
                : autoRotateValue == DEVICE_STATE_ROTATION_LOCK_LOCKED;
    }

    @Override
    public Boolean isRotationLockedForAllStates() {
        final SparseIntArray deviceStateAutoRotateSetting = getRotationLockSetting();
        if (deviceStateAutoRotateSetting == null) return null;
        for (int i = 0; i < deviceStateAutoRotateSetting.size(); i++) {
            if (deviceStateAutoRotateSetting.valueAt(i) != DEVICE_STATE_ROTATION_LOCK_LOCKED) {
                return false;
            }
        }
        return true;
    }

    @NonNull
    @Override
    public List<SettableDeviceState> getSettableDeviceStates() {
        return mSettableDeviceState;
    }

    @Override
    public void updateSetting(int deviceState, boolean autoRotate) {
        // TODO: b/350946537 - Create IPC to update the setting, and call it here.
        throw new UnsupportedOperationException("API updateSetting is not implemented yet");
    }

    @Override
    public void dump(@NonNull PrintWriter writer, String[] args) {
        IndentingPrintWriter indentingWriter = new IndentingPrintWriter(writer, "  ");
        indentingWriter.println("DeviceStateAutoRotateSettingManagerImpl");
        indentingWriter.increaseIndent();
        indentingWriter.println("fallbackPostureMap: " + mFallbackPostureMap);
        indentingWriter.println("settableDeviceState: " + mSettableDeviceState);
        indentingWriter.decreaseIndent();
    }

    @NonNull
    @Override
    public SparseIntArray getDefaultRotationLockSetting() {
        return mDefaultDeviceStateAutoRotateSetting.clone();
    }

    private void notifyListeners() {
        for (DeviceStateAutoRotateSettingListener listener : mSettingListeners) {
            listener.onSettingsChanged();
        }
    }

    /**
     * Loads the {@link R.array#config_perDeviceStateRotationLockDefaults} array and populates the
     * {@link #mFallbackPostureMap}, {@link #mSettableDeviceState}, and
     * {@link #mDefaultDeviceStateAutoRotateSetting}
     * fields.
     */
    private void loadAutoRotateDeviceStates(Context context) {
        final String[] perDeviceStateAutoRotateDefaults =
                context.getResources().getStringArray(
                        R.array.config_perDeviceStateRotationLockDefaults);
        for (String entry : perDeviceStateAutoRotateDefaults) {
            final PostureEntry parsedEntry = parsePostureEntry(entry);
            if (parsedEntry == null) return;

            final int posture = parsedEntry.posture;
            final int autoRotateValue = parsedEntry.autoRotateValue;
            final Integer fallbackPosture = parsedEntry.fallbackPosture;
            final Integer deviceState = mPosturesHelper.postureToDeviceState(posture);

            if (deviceState == null) {
                Log.wtf(TAG, "No matching device state for posture: " + posture);
            } else {
                mSettableDeviceState.add(new SettableDeviceState(deviceState,
                        autoRotateValue != DEVICE_STATE_ROTATION_LOCK_IGNORED)
                );
            }

            if (autoRotateValue == DEVICE_STATE_ROTATION_LOCK_IGNORED
                    && fallbackPosture != null) {
                mFallbackPostureMap.put(posture, fallbackPosture);
            } else if (autoRotateValue == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
                Log.w(TAG, "Auto rotate setting is IGNORED, but no fallback-posture defined");
            }
            mDefaultDeviceStateAutoRotateSetting.put(posture, autoRotateValue);
        }
    }

    private PostureEntry parsePostureEntry(String entry) {
        final String[] values = entry.split(SEPARATOR_REGEX);
        if (values.length < 2 || values.length > 3) { // It should contain 2 or 3 values.
            Log.wtf(TAG, "Invalid number of values in entry: " + entry);
            return null;
        }
        try {
            final int posture = Integer.parseInt(values[0]);
            final int autoRotateValue = Integer.parseInt(values[1]);
            final Integer fallbackPosture = (values.length == 3) ? Integer.parseInt(values[2])
                    : null;

            return new PostureEntry(posture, autoRotateValue, fallbackPosture);

        } catch (NumberFormatException e) {
            Log.wtf(TAG, "Invalid number format in '" + entry + "'", e);
            return null;
        }
    }


    private Integer extractSettingForDevicePosture(
            int devicePosture,
            SparseIntArray deviceStateAutoRotateSetting
    ) {
        return deviceStateAutoRotateSetting == null ? null : deviceStateAutoRotateSetting.get(
                devicePosture,
                DEVICE_STATE_ROTATION_LOCK_IGNORED);
    }

    private record PostureEntry(int posture, int autoRotateValue, Integer fallbackPosture) {
    }
}
+0 −241

File deleted.

Preview size limit exceeded, changes collapsed.

+57 −0
Original line number Diff line number Diff line
@@ -14,40 +14,44 @@
 * limitations under the License.
 */

package com.android.settingslib.devicestate
package com.android.settingslib.devicestate;

import android.content.Context
import android.os.Handler
import com.android.window.flags.Flags
import java.util.concurrent.Executor
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;

import com.android.window.flags.Flags;

import java.util.concurrent.Executor;

/**
 * Provides appropriate instance of [DeviceStateAutoRotateSettingManager], based on the value of
 * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR].
 * Provides appropriate instance of {@link DeviceStateAutoRotateSettingManager}, based on the value
 * of {@link Flags#FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR}.
 */
object DeviceStateAutoRotateSettingManagerProvider {
public class DeviceStateAutoRotateSettingManagerProvider {
    /**
     * Provides an instance of [DeviceStateAutoRotateSettingManager], based on the value of
     * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. It is supposed to be used
     * by apps that supports dagger.
     * Provides an instance of {@link DeviceStateAutoRotateSettingManager}, based on the value of
     * {@link Flags#FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR}. It is supposed to be
     * used by apps that supports dagger.
     */
    @JvmStatic
    fun createInstance(
        context: Context,
        backgroundExecutor: Executor,
        secureSettings: SecureSettings,
        mainHandler: Handler,
        posturesHelper: PosturesHelper,
    ): DeviceStateAutoRotateSettingManager =
    @NonNull
    public static DeviceStateAutoRotateSettingManager createInstance(
            @NonNull Context context,
            @NonNull Executor backgroundExecutor,
            @NonNull SecureSettings secureSettings,
            @NonNull Handler mainHandler,
            @NonNull PosturesHelper posturesHelper
    ) {
        if (Flags.enableDeviceStateAutoRotateSettingRefactor()) {
            DeviceStateAutoRotateSettingManagerImpl(
            return new DeviceStateAutoRotateSettingManagerImpl(
                    context,
                    backgroundExecutor,
                    secureSettings,
                    mainHandler,
                posturesHelper,
            )
                    posturesHelper
            );
        } else {
            DeviceStateRotationLockSettingsManager(context, secureSettings)
            return new DeviceStateRotationLockSettingsManager(context, secureSettings);
        }
    }
}
Loading