All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowUIModeManager Maven / Gradle / Ivy

package org.robolectric.shadows;

import static android.os.Build.VERSION_CODES.TIRAMISU;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.annotation.SystemApi;
import android.app.UiModeManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import androidx.annotation.GuardedBy;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Set;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;

/** Shadow for {@link UiModeManager}. */
@Implements(UiModeManager.class)
public class ShadowUIModeManager {
  public int currentModeType = Configuration.UI_MODE_TYPE_UNDEFINED;
  public int currentNightMode = UiModeManager.MODE_NIGHT_AUTO;
  public int lastFlags;
  public int lastCarModePriority;
  private int currentApplicationNightMode = 0;
  private final Set activeProjectionTypes = new HashSet<>();
  private boolean failOnProjectionToggle;

  private static final ImmutableSet VALID_NIGHT_MODES =
      ImmutableSet.of(
          UiModeManager.MODE_NIGHT_AUTO, UiModeManager.MODE_NIGHT_NO, UiModeManager.MODE_NIGHT_YES);

  private static final int DEFAULT_PRIORITY = 0;

  private final Object lock = new Object();

  @GuardedBy("lock")
  private int nightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;

  @GuardedBy("lock")
  private boolean isNightModeOn = false;

  @RealObject UiModeManager realUiModeManager;

  private static final ImmutableSet VALID_NIGHT_MODE_CUSTOM_TYPES =
      ImmutableSet.of(
          UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE,
          UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME);

  @Implementation
  protected int getCurrentModeType() {
    return currentModeType;
  }

  public void setCurrentModeType(int modeType) {
    this.currentModeType = modeType;
  }

  @Implementation(maxSdk = VERSION_CODES.Q)
  protected void enableCarMode(int flags) {
    enableCarMode(DEFAULT_PRIORITY, flags);
  }

  @Implementation(minSdk = VERSION_CODES.R)
  protected void enableCarMode(int priority, int flags) {
    currentModeType = Configuration.UI_MODE_TYPE_CAR;
    lastCarModePriority = priority;
    lastFlags = flags;
  }

  @Implementation
  protected void disableCarMode(int flags) {
    currentModeType = Configuration.UI_MODE_TYPE_NORMAL;
    lastFlags = flags;
  }

  @Implementation
  protected int getNightMode() {
    return currentNightMode;
  }

  @Implementation
  protected void setNightMode(int mode) {
    synchronized (lock) {
      ContentResolver resolver = getContentResolver();
      switch (mode) {
        case UiModeManager.MODE_NIGHT_NO:
        case UiModeManager.MODE_NIGHT_YES:
        case UiModeManager.MODE_NIGHT_AUTO:
          currentNightMode = mode;
          nightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
          if (resolver != null) {
            Settings.Secure.putInt(resolver, Settings.Secure.UI_NIGHT_MODE, mode);
            Settings.Secure.putInt(
                resolver,
                Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE,
                UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
          }
          break;
        default:
          currentNightMode = UiModeManager.MODE_NIGHT_AUTO;
          if (resolver != null) {
            Settings.Secure.putInt(
                resolver, Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
          }
      }
    }
  }

  public int getApplicationNightMode() {
    return currentApplicationNightMode;
  }

  public Set getActiveProjectionTypes() {
    return new HashSet<>(activeProjectionTypes);
  }

  public void setFailOnProjectionToggle(boolean failOnProjectionToggle) {
    this.failOnProjectionToggle = failOnProjectionToggle;
  }

  @Implementation(minSdk = VERSION_CODES.S)
  @HiddenApi
  protected void setApplicationNightMode(int mode) {
    currentApplicationNightMode = mode;
  }

  @Implementation(minSdk = VERSION_CODES.S)
  @SystemApi
  protected boolean requestProjection(int projectionType) {
    if (projectionType == UiModeManager.PROJECTION_TYPE_AUTOMOTIVE) {
      assertHasPermission(android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION);
    }
    if (failOnProjectionToggle) {
      return false;
    }
    activeProjectionTypes.add(projectionType);
    return true;
  }

  @Implementation(minSdk = VERSION_CODES.S)
  @SystemApi
  protected boolean releaseProjection(int projectionType) {
    if (projectionType == UiModeManager.PROJECTION_TYPE_AUTOMOTIVE) {
      assertHasPermission(android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION);
    }
    if (failOnProjectionToggle) {
      return false;
    }
    return activeProjectionTypes.remove(projectionType);
  }

  @Implementation(minSdk = TIRAMISU)
  protected int getNightModeCustomType() {
    synchronized (lock) {
      return nightModeCustomType;
    }
  }

  /** Returns whether night mode is currently on when a custom night mode type is selected. */
  public boolean isNightModeOn() {
    synchronized (lock) {
      return isNightModeOn;
    }
  }

  @Implementation(minSdk = TIRAMISU)
  protected void setNightModeCustomType(int mode) {
    synchronized (lock) {
      ContentResolver resolver = getContentResolver();
      if (VALID_NIGHT_MODE_CUSTOM_TYPES.contains(mode)) {
        nightModeCustomType = mode;
        currentNightMode = UiModeManager.MODE_NIGHT_CUSTOM;
        if (resolver != null) {
          Settings.Secure.putInt(resolver, Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE, mode);
        }
      } else {
        nightModeCustomType = UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN;
        if (resolver != null) {
          Settings.Secure.putInt(
              resolver,
              Settings.Secure.UI_NIGHT_MODE_CUSTOM_TYPE,
              UiModeManager.MODE_NIGHT_CUSTOM_TYPE_UNKNOWN);
        }
      }
    }
  }

  private ContentResolver getContentResolver() {
    Context context = getContext();
    return context == null ? null : context.getContentResolver();
  }

  // Note: UiModeManager stores the context only starting from Android R.
  private Context getContext() {
    if (VERSION.SDK_INT < VERSION_CODES.R) {
      return null;
    }
    return reflector(UiModeManagerReflector.class, realUiModeManager).getContext();
  }

  @Implementation(minSdk = TIRAMISU)
  protected boolean setNightModeActivatedForCustomMode(int mode, boolean active) {
    synchronized (lock) {
      if (VALID_NIGHT_MODE_CUSTOM_TYPES.contains(mode) && nightModeCustomType == mode) {
        isNightModeOn = active;
        return true;
      }
      return false;
    }
  }

  @ForType(UiModeManager.class)
  interface UiModeManagerReflector {
    @Accessor("mContext")
    Context getContext();
  }

  private void assertHasPermission(String... permissions) {
    Context context = RuntimeEnvironment.getApplication();
    for (String permission : permissions) {
      if (context.getPackageManager().checkPermission(permission, context.getPackageName())
          != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Missing required permission: " + permission);
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy