Merge "Clear set of "unseen" notifications when unlocked" into tm-qpr-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8c00c6a..c0e89d2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3452,7 +3452,7 @@
public class WindowOrganizer {
ctor public WindowOrganizer();
- method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
}
diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java
index 12ad914..c8f6327 100644
--- a/core/java/android/window/TaskFragmentAnimationParams.java
+++ b/core/java/android/window/TaskFragmentAnimationParams.java
@@ -33,6 +33,13 @@
public static final TaskFragmentAnimationParams DEFAULT =
new TaskFragmentAnimationParams.Builder().build();
+ /**
+ * The default value for animation background color, which means to use the theme window
+ * background color.
+ */
+ @ColorInt
+ public static final int DEFAULT_ANIMATION_BACKGROUND_COLOR = 0;
+
@ColorInt
private final int mAnimationBackgroundColor;
@@ -104,12 +111,13 @@
public static final class Builder {
@ColorInt
- private int mAnimationBackgroundColor = 0;
+ private int mAnimationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
/**
* Sets the {@link ColorInt} to use for the background during the animation with this
* TaskFragment if the animation requires a background. The default value is
- * {@code 0}, which is to use the theme window background.
+ * {@link #DEFAULT_ANIMATION_BACKGROUND_COLOR}, which is to use the theme window background
+ * color.
*
* @param color a packed color int, {@code AARRGGBB}, for the animation background color.
* @return this {@link Builder}.
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 2a80d02..740fbac 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -61,9 +61,7 @@
* Apply multiple WindowContainer operations at once.
*
* Note that using this API requires the caller to hold
- * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
- * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
- * created by itself.
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}.
*
* @param t The transaction to apply.
* @param callback This transaction will use the synchronization scheme described in
@@ -72,8 +70,7 @@
* @return An ID for the sync operation which will later be passed to transactionReady callback.
* This lets the caller differentiate overlapping sync operations.
*/
- @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
- conditional = true)
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
@NonNull WindowContainerTransactionCallback callback) {
try {
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
index 205c5fd..d2b612a 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -73,6 +73,23 @@
mOnPropertiesChangedListener);
}
+ public void registerForCurrentUser() {
+ ContentResolver r = mContext.getContentResolver();
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+ false, this);
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+ false, this);
+ r.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+ false, this);
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ runnable -> mMainHandler.post(runnable),
+ mOnPropertiesChangedListener);
+ }
+
public void unregister() {
mContext.getContentResolver().unregisterContentObserver(this);
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b1610d7..8952f37 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -428,7 +428,7 @@
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
} else if (err != NO_ERROR) {
- jniThrowException(env, OutOfResourcesException, NULL);
+ jniThrowException(env, OutOfResourcesException, statusToString(err).c_str());
return 0;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dffd1cc..dafa0ad 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6084,6 +6084,12 @@
different from the home screen wallpaper. -->
<bool name="config_independentLockscreenLiveWallpaper">false</bool>
+ <!-- Whether the vendor power press code need to be mapped. -->
+ <bool name="config_powerPressMapping">false</bool>
+
+ <!-- Power press vendor code. -->
+ <integer name="config_powerPressCode">-1</integer>
+
<!-- Whether to show weather on the lock screen by default. -->
<bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9cc8aa8..591ba5f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2655,6 +2655,8 @@
<java-symbol type="integer" name="config_sideFpsToastTimeout"/>
<java-symbol type="integer" name="config_sidefpsSkipWaitForPowerAcquireMessage"/>
<java-symbol type="integer" name="config_sidefpsSkipWaitForPowerVendorAcquireMessage"/>
+ <java-symbol type="integer" name="config_powerPressCode"/>
+ <java-symbol type="bool" name="config_powerPressMapping"/>
<!-- Clickable toast used during sidefps enrollment -->
<java-symbol type="layout" name="side_fps_toast" />
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 6852320..c3b6916 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 111cfd8..f11836e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -650,7 +650,6 @@
}
mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
- mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
// If the displayId of the task is different than what PipBoundsHandler has, then update
// it. This is possible if we entered PiP on an external display.
@@ -659,6 +658,17 @@
mOnDisplayIdChangeCallback.accept(info.displayId);
}
+ // UiEvent logging.
+ final PipUiEventLogger.PipUiEventEnum uiEventEnum;
+ if (isLaunchIntoPipTask()) {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER_CONTENT_PIP;
+ } else if (mPipTransitionState.getInSwipePipToHomeTransition()) {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER;
+ } else {
+ uiEventEnum = PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER;
+ }
+ mPipUiEventLoggerLogger.log(uiEventEnum);
+
if (mPipTransitionState.getInSwipePipToHomeTransition()) {
if (!mWaitForFixedRotation) {
onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
index 513ebba..3e5a19b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java
@@ -78,6 +78,12 @@
@UiEvent(doc = "Activity enters picture-in-picture mode")
PICTURE_IN_PICTURE_ENTER(603),
+ @UiEvent(doc = "Activity enters picture-in-picture mode with auto-enter-pip API")
+ PICTURE_IN_PICTURE_AUTO_ENTER(1313),
+
+ @UiEvent(doc = "Activity enters picture-in-picture mode from content-pip API")
+ PICTURE_IN_PICTURE_ENTER_CONTENT_PIP(1314),
+
@UiEvent(doc = "Expands from picture-in-picture to fullscreen")
PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604),
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 90c4440..2ab7a58 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -174,14 +174,13 @@
void ShaderCache::saveToDiskLocked() {
ATRACE_NAME("ShaderCache::saveToDiskLocked");
- if (mInitialized && mBlobCache && mSavePending) {
+ if (mInitialized && mBlobCache) {
if (mIDHash.size()) {
auto key = sIDKey;
set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
}
mBlobCache->writeToFile();
}
- mSavePending = false;
}
void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
@@ -224,10 +223,10 @@
}
set(bc, key.data(), keySize, value, valueSize);
- if (!mSavePending && mDeferredSaveDelay > 0) {
+ if (!mSavePending && mDeferredSaveDelayMs > 0) {
mSavePending = true;
std::thread deferredSaveThread([this]() {
- sleep(mDeferredSaveDelay);
+ usleep(mDeferredSaveDelayMs * 1000); // milliseconds to microseconds
std::lock_guard<std::mutex> lock(mMutex);
// Store file on disk if there a new shader or Vulkan pipeline cache size changed.
if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
@@ -236,6 +235,7 @@
mTryToStorePipelineCache = false;
mCacheDirty = false;
}
+ mSavePending = false;
});
deferredSaveThread.detach();
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 3e0fd51..4e3eb81 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -153,7 +153,8 @@
* pending. Each time a key/value pair is inserted into the cache via
* load, a deferred save is initiated if one is not already pending.
* This will wait some amount of time and then trigger a save of the cache
- * contents to disk.
+ * contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
+ * is disabled.
*/
bool mSavePending = false;
@@ -163,9 +164,11 @@
size_t mObservedBlobValueSize = 20 * 1024;
/**
- * The time in seconds to wait before saving newly inserted cache entries.
+ * The time in milliseconds to wait before saving newly inserted cache entries.
+ *
+ * WARNING: setting this to 0 will disable writing the cache to disk.
*/
- unsigned int mDeferredSaveDelay = 4;
+ unsigned int mDeferredSaveDelayMs = 4 * 1000;
/**
* "mMutex" is the mutex used to prevent concurrent access to the member
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 974d85a..7bcd45c 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <GrDirectContext.h>
+#include <Properties.h>
+#include <SkData.h>
+#include <SkRefCnt.h>
#include <cutils/properties.h>
#include <dirent.h>
#include <errno.h>
@@ -22,9 +26,12 @@
#include <stdlib.h>
#include <sys/types.h>
#include <utils/Log.h>
+
#include <cstdint>
+
#include "FileBlobCache.h"
#include "pipeline/skia/ShaderCache.h"
+#include "tests/common/TestUtils.h"
using namespace android::uirenderer::skiapipeline;
@@ -35,11 +42,38 @@
class ShaderCacheTestUtils {
public:
/**
- * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
- * If set to 0, then deferred save is disabled.
+ * Hack to reset all member variables of the given cache to their default / initial values.
+ *
+ * WARNING: this must be kept up to date manually, since ShaderCache's parent disables just
+ * reassigning a new instance.
*/
- static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
- cache.mDeferredSaveDelay = saveDelay;
+ static void reinitializeAllFields(ShaderCache& cache) {
+ ShaderCache newCache = ShaderCache();
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ // By order of declaration
+ cache.mInitialized = newCache.mInitialized;
+ cache.mBlobCache.reset(nullptr);
+ cache.mFilename = newCache.mFilename;
+ cache.mIDHash.clear();
+ cache.mSavePending = newCache.mSavePending;
+ cache.mObservedBlobValueSize = newCache.mObservedBlobValueSize;
+ cache.mDeferredSaveDelayMs = newCache.mDeferredSaveDelayMs;
+ cache.mTryToStorePipelineCache = newCache.mTryToStorePipelineCache;
+ cache.mInStoreVkPipelineInProgress = newCache.mInStoreVkPipelineInProgress;
+ cache.mNewPipelineCacheSize = newCache.mNewPipelineCacheSize;
+ cache.mOldPipelineCacheSize = newCache.mOldPipelineCacheSize;
+ cache.mCacheDirty = newCache.mCacheDirty;
+ cache.mNumShadersCachedInRam = newCache.mNumShadersCachedInRam;
+ }
+
+ /**
+ * "setSaveDelayMs" sets the time in milliseconds to wait before saving newly inserted cache
+ * entries. If set to 0, then deferred save is disabled, and "saveToDiskLocked" must be called
+ * manually, as seen in the "terminate" testing helper function.
+ */
+ static void setSaveDelayMs(ShaderCache& cache, unsigned int saveDelayMs) {
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ cache.mDeferredSaveDelayMs = saveDelayMs;
}
/**
@@ -48,8 +82,9 @@
*/
static void terminate(ShaderCache& cache, bool saveContent) {
std::lock_guard<std::mutex> lock(cache.mMutex);
- cache.mSavePending = saveContent;
- cache.saveToDiskLocked();
+ if (saveContent) {
+ cache.saveToDiskLocked();
+ }
cache.mBlobCache = NULL;
}
@@ -60,6 +95,38 @@
static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
return cache.validateCache(hash.data(), hash.size() * sizeof(T));
}
+
+ /**
+ * Waits until cache::mSavePending is false, checking every 0.1 ms *while the mutex is free*.
+ *
+ * Fails if there was no save pending, or if the cache was already being written to disk, or if
+ * timeoutMs is exceeded.
+ *
+ * Note: timeoutMs only guards against mSavePending getting stuck like in b/268205519, and
+ * cannot protect against mutex-based deadlock. Reaching timeoutMs implies something is broken,
+ * so setting it to a sufficiently large value will not delay execution in the happy state.
+ */
+ static void waitForPendingSave(ShaderCache& cache, const int timeoutMs = 50) {
+ {
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ ASSERT_TRUE(cache.mSavePending);
+ }
+ bool saving = true;
+ float elapsedMilliseconds = 0;
+ while (saving) {
+ if (elapsedMilliseconds >= timeoutMs) {
+ FAIL() << "Timed out after waiting " << timeoutMs << " ms for a pending save";
+ }
+ // This small (0.1 ms) delay is to avoid working too much while waiting for
+ // deferredSaveThread to take the mutex and start the disk write.
+ const int delayMicroseconds = 100;
+ usleep(delayMicroseconds);
+ elapsedMilliseconds += (float)delayMicroseconds / 1000;
+
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ saving = cache.mSavePending;
+ }
+ }
};
} /* namespace skiapipeline */
@@ -81,6 +148,18 @@
return false;
}
+/**
+ * Attempts to delete the given file, and asserts that either:
+ * 1. Deletion was successful, OR
+ * 2. The file did not exist.
+ *
+ * Tip: wrap calls to this in ASSERT_NO_FATAL_FAILURE(x) if a test should exit early if this fails.
+ */
+void deleteFileAssertSuccess(const std::string& filePath) {
+ int deleteResult = remove(filePath.c_str());
+ ASSERT_TRUE(0 == deleteResult || ENOENT == errno);
+}
+
inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
0 == memcmp(shader1->data(), shader2->data(), shader1->size());
@@ -91,6 +170,10 @@
return checkShader(shader, shader2);
}
+inline bool checkShader(const sk_sp<SkData>& shader, const std::string& program) {
+ return checkShader(shader, program.c_str());
+}
+
template <typename T>
bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
@@ -101,6 +184,10 @@
shader = SkData::MakeWithCString(program);
}
+void setShader(sk_sp<SkData>& shader, const std::string& program) {
+ setShader(shader, program.c_str());
+}
+
template <typename T>
void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
@@ -124,13 +211,13 @@
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
// remove any test files from previous test run
- int deleteFile = remove(cacheFile1.c_str());
- ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
std::srand(0);
// read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
+ ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0); // disable deferred save
ShaderCache::get().initShaderDiskCache();
// read a key - should not be found since the cache is empty
@@ -184,7 +271,8 @@
ASSERT_TRUE(checkShader(outVS2, dataBuffer));
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
- remove(cacheFile1.c_str());
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
}
TEST(ShaderCacheTest, testCacheValidation) {
@@ -196,13 +284,13 @@
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
// remove any test files from previous test run
- int deleteFile = remove(cacheFile1.c_str());
- ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
std::srand(0);
// generate identity and read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
- ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
+ ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0); // disable deferred save
std::vector<uint8_t> identity(1024);
genRandomData(identity);
ShaderCache::get().initShaderDiskCache(
@@ -276,7 +364,81 @@
}
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
- remove(cacheFile1.c_str());
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
+}
+
+using namespace android::uirenderer;
+RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+ if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+ // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+ GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
+ }
+ if (!folderExist(getExternalStorageFolder())) {
+ // Don't run the test if external storage folder is not available
+ return;
+ }
+ std::string cacheFile = getExternalStorageFolder() + "/shaderCacheTest";
+ GrDirectContext* grContext = renderThread.getGrContext();
+
+ // Remove any test files from previous test run
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
+
+ // The first iteration of this loop is to save an initial VkPipelineCache data blob to disk,
+ // which sets up the second iteration for a common scenario of comparing a "new" VkPipelineCache
+ // blob passed to "store" against the same blob that's already in the persistent cache from a
+ // previous launch. "reinitializeAllFields" is critical to emulate each iteration being as close
+ // to the state of a freshly launched app as possible, as the initial values of member variables
+ // like mInStoreVkPipelineInProgress and mOldPipelineCacheSize are critical to catch issues
+ // such as b/268205519
+ for (int flushIteration = 1; flushIteration <= 2; flushIteration++) {
+ SCOPED_TRACE("Frame flush iteration " + std::to_string(flushIteration));
+ // Reset *all* in-memory data and reload the cache from disk.
+ ShaderCacheTestUtils::reinitializeAllFields(ShaderCache::get());
+ ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 10); // Delay must be > 0 to save.
+ ShaderCache::get().setFilename(cacheFile.c_str());
+ ShaderCache::get().initShaderDiskCache();
+
+ // 1st iteration: store pipeline data to be read back on a subsequent "boot" of the "app".
+ // 2nd iteration: ensure that an initial frame flush (without storing any shaders) given the
+ // same pipeline data that's already on disk doesn't break the cache.
+ ShaderCache::get().onVkFrameFlushed(grContext);
+ ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+ }
+
+ constexpr char shader1[] = "sassas";
+ constexpr char shader2[] = "someVS";
+ constexpr int numIterations = 3;
+ // Also do n iterations of separate "store some shaders then flush the frame" pairs to just
+ // double-check the cache also doesn't get stuck from that use case.
+ for (int saveIteration = 1; saveIteration <= numIterations; saveIteration++) {
+ SCOPED_TRACE("Shader save iteration " + std::to_string(saveIteration));
+ // Write twice to the in-memory cache, which should start a deferred save with both queued.
+ sk_sp<SkData> inVS;
+ setShader(inVS, shader1 + std::to_string(saveIteration));
+ ShaderCache::get().store(GrProgramDescTest(100), *inVS.get(), SkString());
+ setShader(inVS, shader2 + std::to_string(saveIteration));
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
+
+ // Simulate flush to also save latest pipeline info.
+ ShaderCache::get().onVkFrameFlushed(grContext);
+ ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+ }
+
+ // Reload from disk to ensure saving succeeded.
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ ShaderCache::get().initShaderDiskCache();
+
+ // Read twice, ensure equal to last store.
+ sk_sp<SkData> outVS;
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, shader1 + std::to_string(numIterations)));
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, shader2 + std::to_string(numIterations)));
+
+ // Clean up.
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
}
} // namespace
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index 4cb765d..488f8c7 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -301,9 +301,13 @@
* Use only `handleUpdateState` to modify the values of the state to the new ones. This can be done by polling controllers or through the `arg` parameter.
* If the controller is not a `CallbackController`, respond to `handleSetListening` by attaching/dettaching from controllers.
* Implement `isAvailable` so the tile will not be created when it's not necessary.
-4. In `QSFactoryImpl`:
- * Inject a `Provider` for the tile created before.
- * Add a case to the `switch` with a unique String spec for the chosen tile.
+4. Either create a new feature module or find an existing related feature module and add the following binding method:
+ * ```kotlin
+ @Binds
+ @IntoMap
+ @StringKey(YourNewTile.TILE_SPEC) // A unique word that will map to YourNewTile
+ fun bindYourNewTile(yourNewTile: YourNewTile): QSTileImpl<*>
+ ```
5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles.
6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators.
7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step.
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 1a67691e..2215857 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -452,12 +452,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiConstants.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -743,10 +737,6 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/res/layout/font_scaling_dialog.xml b/packages/SystemUI/res/layout/font_scaling_dialog.xml
new file mode 100644
index 0000000..27c1e9d
--- /dev/null
+++ b/packages/SystemUI/res/layout/font_scaling_dialog.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/font_scaling_slider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ app:max="6"
+ app:progress="0"
+ app:iconStartContentDescription="@string/font_scaling_smaller"
+ app:iconEndContentDescription="@string/font_scaling_larger"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 401dcf7..e6ac59e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2183,6 +2183,14 @@
<!-- Title of the overlay warning the user to interact with the device or it will go to sleep. [CHAR LIMIT=25] -->
<string name="inattentive_sleep_warning_title">Standby</string>
+ <!-- Font scaling -->
+ <!-- Font scaling: Quick Settings dialog title [CHAR LIMIT=30] -->
+ <string name="font_scaling_dialog_title">Font Size</string>
+ <!-- Content Description for the icon button to make fonts smaller. [CHAR LIMIT=30] -->
+ <string name="font_scaling_smaller">Make smaller</string>
+ <!-- Content Description for the icon button to make fonts larger. [CHAR LIMIT=30] -->
+ <string name="font_scaling_larger">Make larger</string>
+
<!-- Window Magnification strings -->
<!-- Title for Magnification Window [CHAR LIMIT=NONE] -->
<string name="magnification_window_title">Magnification Window</string>
@@ -2466,7 +2474,10 @@
<string name="media_output_broadcast_update_error">Can\u2019t save. Try again.</string>
<!-- The error message when Broadcast name/code update failed and can't change again[CHAR LIMIT=60] -->
<string name="media_output_broadcast_last_update_error">Can\u2019t save.</string>
-
+ <!-- The hint message when Broadcast code is less than 4 characters [CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_code_hint_no_less_than_min">Use at least 4 characters</string>
+ <!-- The hint message when Broadcast code is more than 16 characters [CHAR LIMIT=60] -->
+ <string name="media_output_broadcast_code_hint_no_more_than_max">Use fewer than 16 characters</string>
<!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_clip_data_label">Build number</string>
@@ -2831,4 +2842,19 @@
[CHAR LIMIT=32]
-->
<string name="lock_screen_settings">Lock screen settings</string>
+
+ <!-- Content description for Wi-Fi not available icon on dream [CHAR LIMIT=NONE]-->
+ <string name="wifi_unavailable_dream_overlay_content_description">Wi-Fi not available</string>
+
+ <!-- Content description for camera blocked icon on dream [CHAR LIMIT=NONE] -->
+ <string name="camera_blocked_dream_overlay_content_description">Camera blocked</string>
+
+ <!-- Content description for camera and microphone blocked icon on dream [CHAR LIMIT=NONE] -->
+ <string name="camera_and_microphone_blocked_dream_overlay_content_description">Camera and microphone blocked</string>
+
+ <!-- Content description for camera and microphone disabled icon on dream [CHAR LIMIT=NONE] -->
+ <string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
+
+ <!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
+ <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 54ae84f9..ead1a10 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -303,9 +303,18 @@
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
- pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=${
- onUnlockIntentWhenBiometricEnrolled.map { BiometricType.values()[it] }
- }")
+
+ val onUnlockIntentWhenBiometricEnrolledString =
+ onUnlockIntentWhenBiometricEnrolled.map {
+ for (biometricType in BiometricType.values()) {
+ if (biometricType.intValue == it) {
+ return@map biometricType.name
+ }
+ }
+ return@map "UNKNOWN"
+ }
+ pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
+ "$onUnlockIntentWhenBiometricEnrolledString")
pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
pw.println(" requestActiveUnlockOnFaceAcquireInfo=" +
"$faceAcquireInfoToTriggerBiometricFailOn")
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
new file mode 100644
index 0000000..799a4d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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.systemui.accessibility
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.ColorCorrectionTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DreamTile
+import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface AccessibilityModule {
+
+ /** Inject ColorInversionTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(ColorInversionTile.TILE_SPEC)
+ fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*>
+
+ /** Inject NightDisplayTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(NightDisplayTile.TILE_SPEC)
+ fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*>
+
+ /** Inject ReduceBrightColorsTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(ReduceBrightColorsTile.TILE_SPEC)
+ fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*>
+
+ /** Inject OneHandedModeTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(OneHandedModeTile.TILE_SPEC)
+ fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*>
+
+ /** Inject ColorCorrectionTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(ColorCorrectionTile.TILE_SPEC)
+ fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*>
+
+ /** Inject DreamTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(DreamTile.TILE_SPEC)
+ fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*>
+
+ /** Inject FontScalingTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(FontScalingTile.TILE_SPEC)
+ fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
new file mode 100644
index 0000000..54f933a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 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.systemui.accessibility.fontscaling
+
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.os.Bundle
+import android.provider.Settings
+import android.view.LayoutInflater
+import android.widget.Button
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.settings.SystemSettings
+
+/** The Dialog that contains a seekbar for changing the font size. */
+class FontScalingDialog(context: Context, private val systemSettings: SystemSettings) :
+ SystemUIDialog(context) {
+ private val strEntryValues: Array<String> =
+ context.resources.getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
+ private lateinit var title: TextView
+ private lateinit var doneButton: Button
+ private lateinit var seekBarWithIconButtonsView: SeekBarWithIconButtonsView
+
+ private val configuration: Configuration =
+ Configuration(context.getResources().getConfiguration())
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setTitle(R.string.font_scaling_dialog_title)
+ setView(LayoutInflater.from(context).inflate(R.layout.font_scaling_dialog, null))
+ setPositiveButton(
+ R.string.quick_settings_done,
+ /* onClick = */ null,
+ /* dismissOnClick = */ true
+ )
+ super.onCreate(savedInstanceState)
+
+ title = requireViewById(com.android.internal.R.id.alertTitle)
+ doneButton = requireViewById(com.android.internal.R.id.button1)
+ seekBarWithIconButtonsView = requireViewById(R.id.font_scaling_slider)
+
+ seekBarWithIconButtonsView.setMax((strEntryValues).size - 1)
+
+ val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, 1.0f)
+ seekBarWithIconButtonsView.setProgress(fontSizeValueToIndex(currentScale))
+
+ seekBarWithIconButtonsView.setOnSeekBarChangeListener(
+ object : OnSeekBarChangeListener {
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ systemSettings.putString(Settings.System.FONT_SCALE, strEntryValues[progress])
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+ // Do nothing
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+ // Do nothing
+ }
+ }
+ )
+ doneButton.setOnClickListener { dismiss() }
+ }
+
+ private fun fontSizeValueToIndex(value: Float): Int {
+ var lastValue = strEntryValues[0].toFloat()
+ for (i in 1 until strEntryValues.size) {
+ val thisValue = strEntryValues[i].toFloat()
+ if (value < lastValue + (thisValue - lastValue) * .5f) {
+ return i - 1
+ }
+ lastValue = thisValue
+ }
+ return strEntryValues.size - 1
+ }
+
+ override fun onConfigurationChanged(configuration: Configuration) {
+ super.onConfigurationChanged(configuration)
+
+ val configDiff = configuration.diff(this.configuration)
+ this.configuration.setTo(configuration)
+
+ if (configDiff and ActivityInfo.CONFIG_FONT_SCALE != 0) {
+ title.post {
+ title.setTextAppearance(R.style.TextAppearance_Dialog_Title)
+ doneButton.setTextAppearance(R.style.Widget_Dialog_Button)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
new file mode 100644
index 0000000..4173790
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.systemui.battery
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.BatterySaverTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface BatterySaverModule {
+
+ /** Inject BatterySaverTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(BatterySaverTile.TILE_SPEC)
+ fun bindBatterySaverTile(batterySaverTile: BatterySaverTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
index e8288a0..8262539 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
@@ -153,6 +153,13 @@
}
/**
+ * Sets max to the seekbar in the layout.
+ */
+ public void setMax(int max) {
+ mSeekbar.setMax(max);
+ }
+
+ /**
* Sets progress to the seekbar in the layout.
* If the progress is smaller than or equals to 0, the IconStart will be disabled. If the
* progress is larger than or equals to Max, the IconEnd will be disabled. The seekbar progress
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 6af8e73..d949d11 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -44,12 +44,15 @@
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.ControlsUiControllerImpl
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.DeviceControlsTile
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
/**
* Module for injecting classes in `com.android.systemui.controls`-
@@ -149,4 +152,9 @@
@IntoMap
@ClassKey(ControlsActivity::class)
abstract fun provideControlsActivity(activity: ControlsActivity): Activity
+
+ @Binds
+ @IntoMap
+ @StringKey(DeviceControlsTile.TILE_SPEC)
+ abstract fun bindDeviceControlsTile(controlsTile: DeviceControlsTile): QSTileImpl<*>
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index fd690df..03a1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -27,6 +27,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.battery.BatterySaverModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -40,6 +41,7 @@
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.rotationlock.RotationLockModule;
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
import com.android.systemui.shade.ShadeController;
@@ -92,11 +94,13 @@
*/
@Module(includes = {
AospPolicyModule.class,
+ BatterySaverModule.class,
GestureModule.class,
MediaModule.class,
PowerModule.class,
QSModule.class,
ReferenceScreenshotModule.class,
+ RotationLockModule.class,
StartCentralSurfacesModule.class,
VolumeModule.class
})
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index cb7c765..bddd8a7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -55,7 +55,6 @@
import com.android.systemui.toast.ToastUI
import com.android.systemui.usb.StorageNotification
import com.android.systemui.util.NotificationChannels
-import com.android.systemui.util.leak.GarbageMonitor
import com.android.systemui.volume.VolumeUI
import com.android.systemui.wmshell.WMShell
import dagger.Binds
@@ -107,12 +106,6 @@
@ClassKey(FsiChromeViewBinder::class)
abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable
- /** Inject into GarbageMonitor.Service. */
- @Binds
- @IntoMap
- @ClassKey(GarbageMonitor::class)
- abstract fun bindGarbageMonitorService(sysui: GarbageMonitor.Service): CoreStartable
-
/** Inject into GlobalActionsComponent. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index c3ee9be..60fccef 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -28,6 +28,7 @@
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
@@ -57,10 +58,12 @@
import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.privacy.PrivacyModule;
+import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
import com.android.systemui.qs.FgsManagerController;
import com.android.systemui.qs.FgsManagerControllerImpl;
import com.android.systemui.qs.footer.dagger.FooterActionsModule;
import com.android.systemui.recents.Recents;
+import com.android.systemui.screenrecord.ScreenRecordModule;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.security.data.repository.SecurityRepositoryModule;
import com.android.systemui.settings.DisplayTracker;
@@ -70,6 +73,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.connectivity.ConnectivityModule;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -85,6 +89,7 @@
import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.PolicyModule;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
@@ -97,6 +102,7 @@
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
import com.android.systemui.util.kotlin.CoroutinesModule;
+import com.android.systemui.util.leak.GarbageMonitorModule;
import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
@@ -126,6 +132,7 @@
* may not appreciate that.
*/
@Module(includes = {
+ AccessibilityModule.class,
AppOpsModule.class,
AssistModule.class,
BiometricsModule.class,
@@ -133,6 +140,7 @@
ClipboardOverlayModule.class,
ClockInfoModule.class,
ClockRegistryModule.class,
+ ConnectivityModule.class,
CoroutinesModule.class,
DreamModule.class,
ControlsModule.class,
@@ -141,17 +149,21 @@
FlagsModule.class,
SystemPropertiesFlagsModule.class,
FooterActionsModule.class,
+ GarbageMonitorModule.class,
LogModule.class,
MediaProjectionModule.class,
MotionToolModule.class,
PeopleHubModule.class,
PeopleModule.class,
PluginModule.class,
+ PolicyModule.class,
PrivacyModule.class,
+ QRCodeScannerModule.class,
ScreenshotModule.class,
SensorModule.class,
MultiUserUtilsModule.class,
SecurityRepositoryModule.class,
+ ScreenRecordModule.class,
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
SmartspaceModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
index 102f208..055cd52 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -16,19 +16,23 @@
package com.android.systemui.dreams;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
import android.util.Log;
import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
import com.android.systemui.dreams.conditions.DreamCondition;
import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* A {@link CoreStartable} to retain a monitor for tracking dreaming.
*/
-public class DreamMonitor implements CoreStartable {
+public class DreamMonitor extends ConditionalCoreStartable {
private static final String TAG = "DreamMonitor";
// We retain a reference to the monitor so it is not garbage-collected.
@@ -39,14 +43,17 @@
@Inject
public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
DreamStatusBarStateCallback callback) {
+ super(pretextMonitor);
mConditionMonitor = monitor;
mDreamCondition = dreamCondition;
mCallback = callback;
}
+
@Override
- public void start() {
+ protected void onStart() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "started");
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 87c5f51..a2dcdf5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -33,8 +34,9 @@
import android.service.dreams.IDreamManager;
import android.util.Log;
-import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import javax.inject.Inject;
import javax.inject.Named;
@@ -43,7 +45,7 @@
* {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
* the designated dream overlay component.
*/
-public class DreamOverlayRegistrant implements CoreStartable {
+public class DreamOverlayRegistrant extends ConditionalCoreStartable {
private static final String TAG = "DreamOverlayRegistrant";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final IDreamManager mDreamManager;
@@ -102,7 +104,9 @@
@Inject
public DreamOverlayRegistrant(Context context, @Main Resources resources,
- @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent) {
+ @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ super(monitor);
mContext = context;
mResources = resources;
mDreamManager = IDreamManager.Stub.asInterface(
@@ -111,7 +115,7 @@
}
@Override
- public void start() {
+ protected void onStart() {
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
filter.addDataSchemeSpecificPart(mOverlayServiceComponent.getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index dd01be0..5aebc32 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -243,6 +243,8 @@
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
mWindow = new PhoneWindow(mContext);
+ // Default to SystemUI name for TalkBack.
+ mWindow.setTitle("");
mWindow.setAttributes(layoutParams);
mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 90c440c..7394e236 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -257,7 +257,8 @@
mConnectivityManager.getActiveNetwork());
final boolean available = capabilities != null
&& capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
- showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+ showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+ R.string.wifi_unavailable_dream_overlay_content_description);
}
private void updateAlarmStatusIcon() {
@@ -294,13 +295,16 @@
@DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
- !micBlocked && cameraBlocked);
+ !micBlocked && cameraBlocked,
+ R.string.camera_blocked_dream_overlay_content_description);
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
- micBlocked && !cameraBlocked);
+ micBlocked && !cameraBlocked,
+ R.string.microphone_blocked_dream_overlay_content_description);
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
- micBlocked && cameraBlocked);
+ micBlocked && cameraBlocked,
+ R.string.camera_and_microphone_blocked_dream_overlay_content_description);
}
private String buildNotificationsContentDescription(int notificationCount) {
@@ -313,11 +317,13 @@
private void updatePriorityModeStatusIcon() {
showIcon(
DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
- mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
+ mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
+ R.string.priority_mode_dream_overlay_content_description);
}
- private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) {
- showIcon(iconType, show, null);
+ private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+ int contentDescriptionResId) {
+ showIcon(iconType, show, mResources.getString(contentDescriptionResId));
}
private void showIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index ee2f1af..244212b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -16,27 +16,31 @@
package com.android.systemui.dreams.complication;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settingslib.dream.DreamBackend;
-import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import com.android.systemui.util.settings.SecureSettings;
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* {@link ComplicationTypesUpdater} observes the state of available complication types set by the
* user, and pushes updates to {@link DreamOverlayStateController}.
*/
@SysUISingleton
-public class ComplicationTypesUpdater implements CoreStartable {
+public class ComplicationTypesUpdater extends ConditionalCoreStartable {
private final DreamBackend mDreamBackend;
private final Executor mExecutor;
private final SecureSettings mSecureSettings;
@@ -48,7 +52,9 @@
DreamBackend dreamBackend,
@Main Executor executor,
SecureSettings secureSettings,
- DreamOverlayStateController dreamOverlayStateController) {
+ DreamOverlayStateController dreamOverlayStateController,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ super(monitor);
mDreamBackend = dreamBackend;
mExecutor = executor;
mSecureSettings = secureSettings;
@@ -56,7 +62,7 @@
}
@Override
- public void start() {
+ public void onStart() {
final ContentObserver settingsObserver = new ContentObserver(null /*handler*/) {
@Override
public void onChange(boolean selfChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
index 77e1fc9..bb1e6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -18,11 +18,14 @@
import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.view.View;
import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import javax.inject.Inject;
import javax.inject.Named;
@@ -60,7 +63,7 @@
* {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with
* SystemUI.
*/
- public static class Registrant implements CoreStartable {
+ public static class Registrant extends ConditionalCoreStartable {
private final DreamOverlayStateController mDreamOverlayStateController;
private final DreamClockTimeComplication mComplication;
@@ -70,13 +73,15 @@
@Inject
public Registrant(
DreamOverlayStateController dreamOverlayStateController,
- DreamClockTimeComplication dreamClockTimeComplication) {
+ DreamClockTimeComplication dreamClockTimeComplication,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ super(monitor);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = dreamClockTimeComplication;
}
@Override
- public void start() {
+ public void onStart() {
mDreamOverlayStateController.addComplication(mComplication);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index 1065b94..7f395d8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -21,6 +21,7 @@
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
import static com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.Context;
import android.content.Intent;
@@ -42,7 +43,9 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import java.util.List;
@@ -75,7 +78,7 @@
/**
* {@link CoreStartable} for registering the complication with SystemUI on startup.
*/
- public static class Registrant implements CoreStartable {
+ public static class Registrant extends ConditionalCoreStartable {
private final DreamHomeControlsComplication mComplication;
private final DreamOverlayStateController mDreamOverlayStateController;
private final ControlsComponent mControlsComponent;
@@ -105,14 +108,16 @@
@Inject
public Registrant(DreamHomeControlsComplication complication,
DreamOverlayStateController dreamOverlayStateController,
- ControlsComponent controlsComponent) {
+ ControlsComponent controlsComponent,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ super(monitor);
mComplication = complication;
mControlsComponent = controlsComponent;
mDreamOverlayStateController = dreamOverlayStateController;
}
@Override
- public void start() {
+ public void onStart() {
mControlsComponent.getControlsListingController().ifPresent(
c -> c.addCallback(mControlsCallback));
mDreamOverlayStateController.addCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index c3aaf0c..e39073b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams.complication;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_SMARTSPACE_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
import android.content.Context;
import android.os.Parcelable;
@@ -28,6 +29,8 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
import java.util.List;
@@ -61,7 +64,7 @@
* {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
* SystemUI.
*/
- public static class Registrant implements CoreStartable {
+ public static class Registrant extends ConditionalCoreStartable {
private final DreamSmartspaceController mSmartSpaceController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final SmartSpaceComplication mComplication;
@@ -81,14 +84,16 @@
public Registrant(
DreamOverlayStateController dreamOverlayStateController,
SmartSpaceComplication smartSpaceComplication,
- DreamSmartspaceController smartSpaceController) {
+ DreamSmartspaceController smartSpaceController,
+ @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+ super(monitor);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = smartSpaceComplication;
mSmartSpaceController = smartSpaceController;
}
@Override
- public void start() {
+ public void onStart() {
mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
@Override
public void onStateChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 0ab8c8e..88c02b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -29,13 +29,20 @@
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
+import com.android.systemui.process.condition.UserProcessCondition;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
import javax.inject.Named;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.IntoSet;
/**
* Dagger Module providing Dream-related functionality.
@@ -54,6 +61,8 @@
String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled";
String DREAM_SUPPORTED = "dream_supported";
+ String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions";
+ String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor";
/**
* Provides the dream component
@@ -112,4 +121,19 @@
static boolean providesDreamSupported(@Main Resources resources) {
return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported);
}
+
+ /** */
+ @Binds
+ @IntoSet
+ @Named(DREAM_PRETEXT_CONDITIONS)
+ Condition bindsUserProcessCondition(UserProcessCondition condition);
+
+ /** */
+ @Provides
+ @Named(DREAM_PRETEXT_MONITOR)
+ static Monitor providesDockerPretextMonitor(
+ @Main Executor executor,
+ @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) {
+ return new Monitor(executor, pretextConditions);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4d89c6c..ff3e729 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -100,7 +100,7 @@
// TODO(b/260335638): Tracking Bug
@JvmField
val NOTIFICATION_INLINE_REPLY_ANIMATION =
- unreleasedFlag(174148361, "notification_inline_reply_animation", teamfood = true)
+ releasedFlag(174148361, "notification_inline_reply_animation")
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
@@ -578,6 +578,12 @@
val CONTROLS_MANAGEMENT_NEW_FLOWS =
unreleasedFlag(2002, "controls_management_new_flows", teamfood = true)
+ // Enables removing app from Home control panel as a part of a new flow
+ // TODO(b/269132640): Tracking Bug
+ @JvmField
+ val APP_PANELS_REMOVE_APPS_ALLOWED =
+ unreleasedFlag(2003, "app_panels_remove_apps_allowed", teamfood = false)
+
// 2100 - Falsing Manager
@JvmField val FALSING_FOR_LONG_TAPS = releasedFlag(2100, "falsing_for_long_taps")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
index 5a9f775..c9f645d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
import com.android.systemui.R
@@ -27,10 +28,14 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
@SysUISingleton
class CameraQuickAffordanceConfig
@@ -39,6 +44,9 @@
@Application private val context: Context,
private val packageManager: PackageManager,
private val cameraGestureHelper: Lazy<CameraGestureHelper>,
+ private val userTracker: UserTracker,
+ private val devicePolicyManager: DevicePolicyManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
override val key: String
@@ -79,7 +87,12 @@
return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
- private fun isLaunchable(): Boolean {
- return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
+ private suspend fun isLaunchable(): Boolean {
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) &&
+ withContext(backgroundDispatcher) {
+ !devicePolicyManager.getCameraDisabled(null, userTracker.userId) &&
+ devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) and
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA == 0
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
index d9ec3b1..6f821a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.Intent
import com.android.systemui.ActivityIntentHelper
@@ -29,10 +30,13 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
@SysUISingleton
class VideoCameraQuickAffordanceConfig
@@ -42,6 +46,8 @@
private val cameraIntents: CameraIntentsWrapper,
private val activityIntentHelper: ActivityIntentHelper,
private val userTracker: UserTracker,
+ private val devicePolicyManager: DevicePolicyManager,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceConfig {
private val intent: Intent by lazy {
@@ -63,8 +69,8 @@
get() = R.drawable.ic_videocam
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState>
- get() =
- flowOf(
+ get() = flow {
+ emit(
if (isLaunchable()) {
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
icon =
@@ -77,6 +83,7 @@
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
}
)
+ }
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (isLaunchable()) {
@@ -95,11 +102,14 @@
)
}
- private fun isLaunchable(): Boolean {
+ private suspend fun isLaunchable(): Boolean {
return activityIntentHelper.getTargetActivityInfo(
intent,
userTracker.userId,
true,
- ) != null
+ ) != null &&
+ withContext(backgroundDispatcher) {
+ !devicePolicyManager.getCameraDisabled(null, userTracker.userId)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 520edef..f5558a2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -1343,9 +1343,9 @@
if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) {
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
} else if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) {
- convertToResumePlayer(removed)
+ convertToResumePlayer(key, removed)
} else if (mediaFlags.isRetainingPlayersEnabled()) {
- handlePossibleRemoval(removed, notificationRemoved = true)
+ handlePossibleRemoval(key, removed, notificationRemoved = true)
} else {
notifyMediaDataRemoved(key)
logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId)
@@ -1359,7 +1359,7 @@
val entry = mediaEntries.remove(key) ?: return
// Clear token since the session is no longer valid
val updated = entry.copy(token = null)
- handlePossibleRemoval(updated)
+ handlePossibleRemoval(key, updated)
}
/**
@@ -1368,8 +1368,11 @@
* if it was removed before becoming inactive. (Assumes that [removed] was removed from
* [mediaEntries] before this function was called)
*/
- private fun handlePossibleRemoval(removed: MediaData, notificationRemoved: Boolean = false) {
- val key = removed.notificationKey!!
+ private fun handlePossibleRemoval(
+ key: String,
+ removed: MediaData,
+ notificationRemoved: Boolean = false
+ ) {
val hasSession = removed.token != null
if (hasSession && removed.semanticActions != null) {
// The app was using session actions, and the session is still valid: keep player
@@ -1395,13 +1398,12 @@
"($hasSession) gone for inactive player $key"
)
}
- convertToResumePlayer(removed)
+ convertToResumePlayer(key, removed)
}
}
/** Set the given [MediaData] as a resume state player and notify listeners */
- private fun convertToResumePlayer(data: MediaData) {
- val key = data.notificationKey!!
+ private fun convertToResumePlayer(key: String, data: MediaData) {
if (DEBUG) Log.d(TAG, "Converting $key to resume")
// Move to resume key (aka package name) if that key doesn't already exist.
val resumeAction = data.resumeAction?.let { getResumeMediaAction(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index b72923a..68d2c5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -164,13 +164,13 @@
mediaCarouselScrollHandler.scrollToStart()
}
}
- private var currentlyExpanded = true
+
+ @VisibleForTesting
+ var currentlyExpanded = true
set(value) {
if (field != value) {
field = value
- for (player in MediaPlayerData.players()) {
- player.setListening(field)
- }
+ updateSeekbarListening(mediaCarouselScrollHandler.visibleToUser)
}
}
@@ -259,6 +259,7 @@
executor,
this::onSwipeToDismiss,
this::updatePageIndicatorLocation,
+ this::updateSeekbarListening,
this::closeGuts,
falsingCollector,
falsingManager,
@@ -590,6 +591,17 @@
?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
}
+ // Check postcondition: mediaContent should have the same number of children as there
+ // are
+ // elements in mediaPlayers.
+ if (MediaPlayerData.players().size != mediaContent.childCount) {
+ Log.e(
+ TAG,
+ "Size of players list and number of views in carousel are out of sync. " +
+ "Players size is ${MediaPlayerData.players().size}. " +
+ "View count is ${mediaContent.childCount}."
+ )
+ }
}
// Returns true if new player is added
@@ -618,7 +630,9 @@
)
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
newPlayer.bindPlayer(data, key)
- newPlayer.setListening(currentlyExpanded)
+ newPlayer.setListening(
+ mediaCarouselScrollHandler.visibleToUser && currentlyExpanded
+ )
MediaPlayerData.addMediaPlayer(
key,
data,
@@ -665,17 +679,6 @@
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaFrame.requiresRemeasuring = true
- // Check postcondition: mediaContent should have the same number of children as there
- // are
- // elements in mediaPlayers.
- if (MediaPlayerData.players().size != mediaContent.childCount) {
- Log.e(
- TAG,
- "Size of players list and number of views in carousel are out of sync. " +
- "Players size is ${MediaPlayerData.players().size}. " +
- "View count is ${mediaContent.childCount}."
- )
- }
return existingPlayer == null
}
@@ -914,6 +917,13 @@
.toFloat()
}
+ /** Update listening to seekbar. */
+ private fun updateSeekbarListening(visibleToUser: Boolean) {
+ for (player in MediaPlayerData.players()) {
+ player.setListening(visibleToUser && currentlyExpanded)
+ }
+ }
+
/** Update the dimension of this carousel. */
private fun updateCarouselDimensions() {
var width = 0
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index 36b2eda..1ace316 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -57,6 +57,7 @@
private val mainExecutor: DelayableExecutor,
val dismissCallback: () -> Unit,
private var translationChangedListener: () -> Unit,
+ private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
@@ -177,6 +178,12 @@
/** Whether the media card is visible to user if any */
var visibleToUser: Boolean = false
+ set(value) {
+ if (field != value) {
+ field = value
+ seekBarUpdateListener.invoke(field)
+ }
+ }
/** Whether the quick setting is expanded or not */
var qsExpanded: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 7f420a8..7677062 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -346,6 +346,11 @@
mSeekBarViewModel.setListening(listening);
}
+ @VisibleForTesting
+ public boolean getListening() {
+ return mSeekBarViewModel.getListening();
+ }
+
/** Sets whether the user is touching the seek bar to change the track position. */
private void setIsScrubbing(boolean isScrubbing) {
if (mMediaData == null || mMediaData.getSemanticActions() == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
index 7db293d..245cf89 100644
--- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -16,11 +16,16 @@
package com.android.systemui.process;
+import javax.inject.Inject;
+
/**
* A simple wrapper that provides access to process-related details. This facilitates testing by
* providing a mockable target around these details.
*/
public class ProcessWrapper {
+ @Inject
+ public ProcessWrapper() {}
+
public int getUserHandleIdentifier() {
return android.os.Process.myUserHandle().getIdentifier();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
new file mode 100644
index 0000000..62c99da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.systemui.qrcodescanner.dagger
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.QRCodeScannerTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface QRCodeScannerModule {
+
+ /**
+ */
+ @Binds
+ @IntoMap
+ @StringKey(QRCodeScannerTile.TILE_SPEC)
+ fun bindQRCodeScannerTile(qrCodeScannerTile: QRCodeScannerTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 25a5c61..27ae171 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.external.QSExternalModule;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -40,11 +41,14 @@
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
+import java.util.Map;
+
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.Multibinds;
/**
* Module for QS dependencies
@@ -53,6 +57,11 @@
includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class})
public interface QSModule {
+ /** A map of internal QS tiles. Ensures that this can be injected even if
+ * it is empty */
+ @Multibinds
+ Map<String, QSTileImpl<?>> tileMap();
+
@Provides
@SysUISingleton
static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index dec6e7d..6b23f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -27,76 +27,32 @@
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.tiles.AirplaneModeTile;
-import com.android.systemui.qs.tiles.AlarmTile;
-import com.android.systemui.qs.tiles.BatterySaverTile;
-import com.android.systemui.qs.tiles.BluetoothTile;
-import com.android.systemui.qs.tiles.CameraToggleTile;
-import com.android.systemui.qs.tiles.CastTile;
-import com.android.systemui.qs.tiles.ColorCorrectionTile;
-import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.DataSaverTile;
-import com.android.systemui.qs.tiles.DeviceControlsTile;
-import com.android.systemui.qs.tiles.DndTile;
-import com.android.systemui.qs.tiles.DreamTile;
-import com.android.systemui.qs.tiles.FlashlightTile;
-import com.android.systemui.qs.tiles.FontScalingTile;
-import com.android.systemui.qs.tiles.HotspotTile;
-import com.android.systemui.qs.tiles.InternetTile;
-import com.android.systemui.qs.tiles.LocationTile;
-import com.android.systemui.qs.tiles.MicrophoneToggleTile;
-import com.android.systemui.qs.tiles.NfcTile;
-import com.android.systemui.qs.tiles.NightDisplayTile;
-import com.android.systemui.qs.tiles.OneHandedModeTile;
-import com.android.systemui.qs.tiles.QRCodeScannerTile;
-import com.android.systemui.qs.tiles.QuickAccessWalletTile;
-import com.android.systemui.qs.tiles.ReduceBrightColorsTile;
-import com.android.systemui.qs.tiles.RotationLockTile;
-import com.android.systemui.qs.tiles.ScreenRecordTile;
-import com.android.systemui.qs.tiles.UiModeNightTile;
-import com.android.systemui.qs.tiles.WorkModeTile;
import com.android.systemui.util.leak.GarbageMonitor;
+import java.util.Map;
+
import javax.inject.Inject;
import javax.inject.Provider;
import dagger.Lazy;
+/**
+ * A factory that creates Quick Settings tiles based on a tileSpec
+ *
+ * To create a new tile within SystemUI, the tile class should extend {@link QSTileImpl} and have
+ * a public static final TILE_SPEC field which serves as a unique key for this tile. (e.g. {@link
+ * com.android.systemui.qs.tiles.DreamTile#TILE_SPEC})
+ *
+ * After, create or find an existing Module class to house the tile's binding method (e.g. {@link
+ * com.android.systemui.accessibility.AccessibilityModule}). If creating a new module, add your
+ * module to the SystemUI dagger graph by including it in an appropriate module.
+ */
@SysUISingleton
public class QSFactoryImpl implements QSFactory {
private static final String TAG = "QSFactory";
- private final Provider<InternetTile> mInternetTileProvider;
- private final Provider<BluetoothTile> mBluetoothTileProvider;
- private final Provider<DndTile> mDndTileProvider;
- private final Provider<ColorCorrectionTile> mColorCorrectionTileProvider;
- private final Provider<ColorInversionTile> mColorInversionTileProvider;
- private final Provider<AirplaneModeTile> mAirplaneModeTileProvider;
- private final Provider<WorkModeTile> mWorkModeTileProvider;
- private final Provider<RotationLockTile> mRotationLockTileProvider;
- private final Provider<FlashlightTile> mFlashlightTileProvider;
- private final Provider<LocationTile> mLocationTileProvider;
- private final Provider<CastTile> mCastTileProvider;
- private final Provider<HotspotTile> mHotspotTileProvider;
- private final Provider<BatterySaverTile> mBatterySaverTileProvider;
- private final Provider<DataSaverTile> mDataSaverTileProvider;
- private final Provider<NightDisplayTile> mNightDisplayTileProvider;
- private final Provider<NfcTile> mNfcTileProvider;
- private final Provider<GarbageMonitor.MemoryTile> mMemoryTileProvider;
- private final Provider<UiModeNightTile> mUiModeNightTileProvider;
- private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
- private final Provider<ReduceBrightColorsTile> mReduceBrightColorsTileProvider;
- private final Provider<CameraToggleTile> mCameraToggleTileProvider;
- private final Provider<MicrophoneToggleTile> mMicrophoneToggleTileProvider;
- private final Provider<DeviceControlsTile> mDeviceControlsTileProvider;
- private final Provider<AlarmTile> mAlarmTileProvider;
- private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
- private final Provider<QRCodeScannerTile> mQRCodeScannerTileProvider;
- private final Provider<OneHandedModeTile> mOneHandedModeTileProvider;
- private final Provider<DreamTile> mDreamTileProvider;
- private final Provider<FontScalingTile> mFontScalingTileProvider;
-
+ protected final Map<String, Provider<QSTileImpl<?>>> mTileMap;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -104,67 +60,10 @@
public QSFactoryImpl(
Lazy<QSHost> qsHostLazy,
Provider<CustomTile.Builder> customTileBuilderProvider,
- Provider<InternetTile> internetTileProvider,
- Provider<BluetoothTile> bluetoothTileProvider,
- Provider<DndTile> dndTileProvider,
- Provider<ColorInversionTile> colorInversionTileProvider,
- Provider<AirplaneModeTile> airplaneModeTileProvider,
- Provider<WorkModeTile> workModeTileProvider,
- Provider<RotationLockTile> rotationLockTileProvider,
- Provider<FlashlightTile> flashlightTileProvider,
- Provider<LocationTile> locationTileProvider,
- Provider<CastTile> castTileProvider,
- Provider<HotspotTile> hotspotTileProvider,
- Provider<BatterySaverTile> batterySaverTileProvider,
- Provider<DataSaverTile> dataSaverTileProvider,
- Provider<NightDisplayTile> nightDisplayTileProvider,
- Provider<NfcTile> nfcTileProvider,
- Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
- Provider<UiModeNightTile> uiModeNightTileProvider,
- Provider<ScreenRecordTile> screenRecordTileProvider,
- Provider<ReduceBrightColorsTile> reduceBrightColorsTileProvider,
- Provider<CameraToggleTile> cameraToggleTileProvider,
- Provider<MicrophoneToggleTile> microphoneToggleTileProvider,
- Provider<DeviceControlsTile> deviceControlsTileProvider,
- Provider<AlarmTile> alarmTileProvider,
- Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
- Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
- Provider<OneHandedModeTile> oneHandedModeTileProvider,
- Provider<ColorCorrectionTile> colorCorrectionTileProvider,
- Provider<DreamTile> dreamTileProvider,
- Provider<FontScalingTile> fontScalingTileProvider) {
+ Map<String, Provider<QSTileImpl<?>>> tileMap) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
-
- mInternetTileProvider = internetTileProvider;
- mBluetoothTileProvider = bluetoothTileProvider;
- mDndTileProvider = dndTileProvider;
- mColorInversionTileProvider = colorInversionTileProvider;
- mAirplaneModeTileProvider = airplaneModeTileProvider;
- mWorkModeTileProvider = workModeTileProvider;
- mRotationLockTileProvider = rotationLockTileProvider;
- mFlashlightTileProvider = flashlightTileProvider;
- mLocationTileProvider = locationTileProvider;
- mCastTileProvider = castTileProvider;
- mHotspotTileProvider = hotspotTileProvider;
- mBatterySaverTileProvider = batterySaverTileProvider;
- mDataSaverTileProvider = dataSaverTileProvider;
- mNightDisplayTileProvider = nightDisplayTileProvider;
- mNfcTileProvider = nfcTileProvider;
- mMemoryTileProvider = memoryTileProvider;
- mUiModeNightTileProvider = uiModeNightTileProvider;
- mScreenRecordTileProvider = screenRecordTileProvider;
- mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider;
- mCameraToggleTileProvider = cameraToggleTileProvider;
- mMicrophoneToggleTileProvider = microphoneToggleTileProvider;
- mDeviceControlsTileProvider = deviceControlsTileProvider;
- mAlarmTileProvider = alarmTileProvider;
- mQuickAccessWalletTileProvider = quickAccessWalletTileProvider;
- mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
- mOneHandedModeTileProvider = oneHandedModeTileProvider;
- mColorCorrectionTileProvider = colorCorrectionTileProvider;
- mDreamTileProvider = dreamTileProvider;
- mFontScalingTileProvider = fontScalingTileProvider;
+ mTileMap = tileMap;
}
/** Creates a tile with a type based on {@code tileSpec} */
@@ -181,63 +80,10 @@
@Nullable
protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
- switch (tileSpec) {
- case "internet":
- return mInternetTileProvider.get();
- case "bt":
- return mBluetoothTileProvider.get();
- case "dnd":
- return mDndTileProvider.get();
- case "inversion":
- return mColorInversionTileProvider.get();
- case "airplane":
- return mAirplaneModeTileProvider.get();
- case "work":
- return mWorkModeTileProvider.get();
- case "rotation":
- return mRotationLockTileProvider.get();
- case "flashlight":
- return mFlashlightTileProvider.get();
- case "location":
- return mLocationTileProvider.get();
- case "cast":
- return mCastTileProvider.get();
- case "hotspot":
- return mHotspotTileProvider.get();
- case "battery":
- return mBatterySaverTileProvider.get();
- case "saver":
- return mDataSaverTileProvider.get();
- case "night":
- return mNightDisplayTileProvider.get();
- case "nfc":
- return mNfcTileProvider.get();
- case "dark":
- return mUiModeNightTileProvider.get();
- case "screenrecord":
- return mScreenRecordTileProvider.get();
- case "reduce_brightness":
- return mReduceBrightColorsTileProvider.get();
- case "cameratoggle":
- return mCameraToggleTileProvider.get();
- case "mictoggle":
- return mMicrophoneToggleTileProvider.get();
- case "controls":
- return mDeviceControlsTileProvider.get();
- case "alarm":
- return mAlarmTileProvider.get();
- case "wallet":
- return mQuickAccessWalletTileProvider.get();
- case "qr_code_scanner":
- return mQRCodeScannerTileProvider.get();
- case "onehanded":
- return mOneHandedModeTileProvider.get();
- case "color_correction":
- return mColorCorrectionTileProvider.get();
- case "dream":
- return mDreamTileProvider.get();
- case "font_scaling":
- return mFontScalingTileProvider.get();
+ if (mTileMap.containsKey(tileSpec)
+ // We should not return a Garbage Monitory Tile if the build is not Debuggable
+ && (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
+ return mTileMap.get(tileSpec).get();
}
// Custom tiles
@@ -246,13 +92,6 @@
mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
}
- // Debug tiles.
- if (Build.IS_DEBUGGABLE) {
- if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
- return mMemoryTileProvider.get();
- }
- }
-
// Broken tiles.
Log.w(TAG, "No stock tile spec: " + tileSpec);
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 033dbe0..92a83bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -57,6 +57,9 @@
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "airplane";
+
private final SettingObserver mSetting;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Lazy<ConnectivityManager> mLazyConnectivityManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 14e0f70..2ca452e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -118,4 +118,8 @@
override fun getLongClickIntent(): Intent? {
return null
}
-}
\ No newline at end of file
+
+ companion object {
+ const val TILE_SPEC = "alarm"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index ee49b29..027a464 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -48,6 +48,8 @@
public class BatterySaverTile extends QSTileImpl<BooleanState> implements
BatteryController.BatteryStateChangeCallback {
+ public static final String TILE_SPEC = "battery";
+
private final BatteryController mBatteryController;
@VisibleForTesting
protected final SettingObserver mSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9a0d0d9..df1c8df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -55,6 +55,9 @@
/** Quick settings tile: Bluetooth **/
public class BluetoothTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "bt";
+
private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
private final BluetoothController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index ee41f1d..93e5f1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -45,6 +45,8 @@
public class CameraToggleTile extends SensorPrivacyToggleTile {
+ public static final String TILE_SPEC = "cameratoggle";
+
@Inject
protected CameraToggleTile(QSHost host,
@Background Looper backgroundLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index dce137f..8d98481 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -66,7 +66,9 @@
/** Quick settings tile: Cast **/
public class CastTile extends QSTileImpl<BooleanState> {
- private static final String INTERACTION_JANK_TAG = "cast";
+ public static final String TILE_SPEC = "cast";
+
+ private static final String INTERACTION_JANK_TAG = TILE_SPEC;
private static final Intent CAST_SETTINGS =
new Intent(Settings.ACTION_CAST_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index 6dfcf5c..b6205d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -48,6 +48,8 @@
/** Quick settings tile: Color correction **/
public class ColorCorrectionTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "color_correction";
+
private final Icon mIcon = ResourceIcon.get(drawable.ic_qs_color_correction);
private final SettingObserver mSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a31500c..9a44e83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -49,6 +49,7 @@
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "inversion";
private final SettingObserver mSetting;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 2fc99f3..add517e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -48,6 +48,8 @@
public class DataSaverTile extends QSTileImpl<BooleanState> implements
DataSaverController.Listener{
+ public static final String TILE_SPEC = "saver";
+
private static final String INTERACTION_JANK_TAG = "start_data_saver";
private final DataSaverController mDataSaverController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 41d8549..01164fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -159,4 +159,8 @@
override fun getTileLabel(): CharSequence {
return mContext.getText(controlsComponent.getTileTitleId())
}
+
+ companion object {
+ const val TILE_SPEC = "controls"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 8b7f53f..434fe45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -67,6 +67,8 @@
/** Quick settings tile: Do not disturb **/
public class DndTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "dnd";
+
private static final Intent ZEN_SETTINGS =
new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
index 5bc209a..53774e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -60,6 +60,8 @@
/** Quick settings tile: Screensaver (dream) **/
public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
+ public static final String TILE_SPEC = "dream";
+
private static final String LOG_TAG = "QSDream";
// TODO: consider 1 animated icon instead
private final Icon mIconDocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index a747926..e091a75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -49,6 +49,7 @@
public class FlashlightTile extends QSTileImpl<BooleanState> implements
FlashlightController.FlashlightListener {
+ public static final String TILE_SPEC = "flashlight";
private final FlashlightController mFlashlightController;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 4d8f89e..721046d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -19,8 +19,12 @@
import android.os.Handler
import android.os.Looper
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
+import com.android.systemui.accessibility.fontscaling.FontScalingDialog
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -30,6 +34,8 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.settings.SystemSettings
import javax.inject.Inject
class FontScalingTile
@@ -42,7 +48,9 @@
metricsLogger: MetricsLogger,
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
- qsLogger: QSLogger
+ qsLogger: QSLogger,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+ private val systemSettings: SystemSettings
) :
QSTileImpl<QSTile.State?>(
host,
@@ -54,7 +62,7 @@
activityStarter,
qsLogger
) {
- private val mIcon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
+ private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
override fun isAvailable(): Boolean {
return false
@@ -66,11 +74,24 @@
return state
}
- override fun handleClick(view: View?) {}
+ override fun handleClick(view: View?) {
+ mUiHandler.post {
+ val dialog: SystemUIDialog = FontScalingDialog(mContext, systemSettings)
+ if (view != null) {
+ dialogLaunchAnimator.showFromView(
+ dialog,
+ view,
+ DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
+ )
+ } else {
+ dialog.show()
+ }
+ }
+ }
override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
state?.label = mContext.getString(R.string.quick_settings_font_scaling_label)
- state?.icon = mIcon
+ state?.icon = icon
}
override fun getLongClickIntent(): Intent? {
@@ -80,4 +101,9 @@
override fun getTileLabel(): CharSequence {
return mContext.getString(R.string.quick_settings_font_scaling_label)
}
+
+ companion object {
+ const val TILE_SPEC = "font_scaling"
+ private const val INTERACTION_JANK_TAG = "font_scaling"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 624def6..6bf8b76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -51,6 +51,7 @@
/** Quick settings tile: Hotspot **/
public class HotspotTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "hotspot";
private final HotspotController mHotspotController;
private final DataSaverController mDataSaverController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1c60486..12e9aeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -67,6 +67,9 @@
/** Quick settings tile: Internet **/
public class InternetTile extends QSTileImpl<SignalState> {
+
+ public static final String TILE_SPEC = "internet";
+
private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
protected final NetworkController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 9466a69..89d402a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -48,6 +48,8 @@
/** Quick settings tile: Location **/
public class LocationTile extends QSTileImpl<BooleanState> {
+ public static final String TILE_SPEC = "location";
+
private final LocationController mController;
private final KeyguardStateController mKeyguard;
private final Callback mCallback = new Callback();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index e547095..2e475d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -45,6 +45,8 @@
public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
+ public static final String TILE_SPEC = "mictoggle";
+
@Inject
protected MicrophoneToggleTile(QSHost host,
@Background Looper backgroundLooper,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index a61f0ce..e189f80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -51,7 +51,9 @@
/** Quick settings tile: Enable/Disable NFC **/
public class NfcTile extends QSTileImpl<BooleanState> {
- private static final String NFC = "nfc";
+ public static final String TILE_SPEC = "nfc";
+
+ private static final String NFC = TILE_SPEC;
private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 0e9f659..aacd53b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -61,6 +61,8 @@
public class NightDisplayTile extends QSTileImpl<BooleanState> implements
NightDisplayListener.Callback {
+ public static final String TILE_SPEC = "night";
+
/**
* Pattern for {@link java.time.format.DateTimeFormatter} used to approximate the time to the
* nearest hour and add on the AM/PM indicator.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
index 7e17124..ae67d99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/OneHandedModeTile.java
@@ -47,6 +47,9 @@
/** Quick settings tile: One-handed mode **/
public class OneHandedModeTile extends QSTileImpl<BooleanState> {
+
+ public static final String TILE_SPEC = "onehanded";
+
private final Icon mIcon = ResourceIcon.get(
com.android.internal.R.drawable.ic_qs_one_handed_mode);
private final SettingObserver mSetting;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
index 6d50b56..92f5272 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java
@@ -44,6 +44,9 @@
/** Quick settings tile: QR Code Scanner **/
public class QRCodeScannerTile extends QSTileImpl<QSTile.State> {
+
+ public static final String TILE_SPEC = "qr_code_scanner";
+
private static final String TAG = "QRCodeScanner";
private final CharSequence mLabel = mContext.getString(R.string.qr_code_scanner_title);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 248c78e..4a3c563 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -62,6 +62,8 @@
/** Quick settings tile: Quick access wallet **/
public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
+ public static final String TILE_SPEC = "wallet";
+
private static final String TAG = "QuickAccessWalletTile";
private static final String FEATURE_CHROME_OS = "org.chromium.arc";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 1dac339..10f1ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -49,6 +49,7 @@
public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
implements ReduceBrightColorsController.Listener{
+ public static final String TILE_SPEC = "reduce_brightness";
private final boolean mIsAvailable;
private final ReduceBrightColorsController mReduceBrightColorsController;
private boolean mIsListening;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 600874f..8888c73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -57,6 +57,9 @@
/** Quick settings tile: Rotation **/
public class RotationLockTile extends QSTileImpl<BooleanState> implements
BatteryController.BatteryStateChangeCallback {
+
+ public static final String TILE_SPEC = "rotation";
+
private static final String EMPTY_SECONDARY_STRING = "";
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index ad00069..07b50c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -54,6 +54,9 @@
*/
public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
implements RecordingController.RecordingStateChangeCallback {
+
+ public static final String TILE_SPEC = "screenrecord";
+
private static final String TAG = "ScreenRecordTile";
private static final String INTERACTION_JANK_TAG = "screen_record";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 92f6690a..809689c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles;
import android.app.UiModeManager;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Handler;
@@ -60,6 +59,9 @@
public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
ConfigurationController.ConfigurationListener,
BatteryController.BatteryStateChangeCallback {
+
+ public static final String TILE_SPEC = "dark";
+
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
private final UiModeManager mUiModeManager;
private final BatteryController mBatteryController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 72c6bfe..6a5c990 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -49,6 +49,9 @@
/** Quick settings tile: Work profile on/off */
public class WorkModeTile extends QSTileImpl<BooleanState> implements
ManagedProfileController.Callback {
+
+ public static final String TILE_SPEC = "work";
+
private final Icon mIcon = ResourceIcon.get(R.drawable.stat_sys_managed_profile_status);
private final ManagedProfileController mProfileController;
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt
new file mode 100644
index 0000000..9abe90f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockModule.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.systemui.rotationlock
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.RotationLockTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface RotationLockModule {
+
+ /** Inject RotationLockTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(RotationLockTile.TILE_SPEC)
+ fun bindRotationLockTile(rotationLockTile: RotationLockTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
new file mode 100644
index 0000000..7467805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.systemui.screenrecord
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.ScreenRecordTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface ScreenRecordModule {
+ /** Inject ScreenRecordTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(ScreenRecordTile.TILE_SPEC)
+ fun bindScreenRecordTile(screenRecordTile: ScreenRecordTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
new file mode 100644
index 0000000..1099810
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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.systemui.statusbar.connectivity
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.AirplaneModeTile
+import com.android.systemui.qs.tiles.BluetoothTile
+import com.android.systemui.qs.tiles.CastTile
+import com.android.systemui.qs.tiles.DataSaverTile
+import com.android.systemui.qs.tiles.HotspotTile
+import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.NfcTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface ConnectivityModule {
+
+ /** Inject InternetTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(InternetTile.TILE_SPEC)
+ fun bindInternetTile(internetTile: InternetTile): QSTileImpl<*>
+
+ /** Inject BluetoothTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(BluetoothTile.TILE_SPEC)
+ fun bindBluetoothTile(bluetoothTile: BluetoothTile): QSTileImpl<*>
+
+ /** Inject CastTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(CastTile.TILE_SPEC)
+ fun bindCastTile(castTile: CastTile): QSTileImpl<*>
+
+ /** Inject HotspotTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(HotspotTile.TILE_SPEC)
+ fun bindHotspotTile(hotspotTile: HotspotTile): QSTileImpl<*>
+
+ /** Inject AirplaneModeTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(AirplaneModeTile.TILE_SPEC)
+ fun bindAirplaneModeTile(airplaneModeTile: AirplaneModeTile): QSTileImpl<*>
+
+ /** Inject DataSaverTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(DataSaverTile.TILE_SPEC)
+ fun bindDataSaverTile(dataSaverTile: DataSaverTile): QSTileImpl<*>
+
+ /** Inject NfcTile into tileMap in QSModule */
+ @Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index 2f34516..16c4027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.pipeline.mobile.data.model
+import android.os.ParcelUuid
+
/**
* SystemUI representation of [SubscriptionInfo]. Currently we only use two fields on the
* subscriptions themselves: subscriptionId and isOpportunistic. Any new fields that we need can be
@@ -29,4 +31,7 @@
* filtering in certain cases. See [MobileIconsInteractor] for the filtering logic
*/
val isOpportunistic: Boolean = false,
+
+ /** Subscriptions in the same group may be filtered or treated as a single subscription */
+ val groupUuid: ParcelUuid? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index 938c734..8f6a87b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -28,8 +28,8 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c9049d8..73ce5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -52,8 +52,8 @@
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -376,6 +376,7 @@
SubscriptionModel(
subscriptionId = subscriptionId,
isOpportunistic = isOpportunistic,
+ groupUuid = groupUuid,
)
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 72d5113..5a2e11e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -150,6 +150,12 @@
val info1 = unfilteredSubs[0]
val info2 = unfilteredSubs[1]
+
+ // Filtering only applies to subscriptions in the same group
+ if (info1.groupUuid == null || info1.groupUuid != info2.groupUuid) {
+ return@combine unfilteredSubs
+ }
+
// If both subscriptions are primary, show both
if (!info1.isOpportunistic && !info2.isOpportunistic) {
return@combine unfilteredSubs
@@ -186,7 +192,7 @@
* validated bit from the old active network (A) while data is changing to the new one (B).
*
* This condition only applies if
- * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
+ * 1. A and B are in the same subscription group (e.g. for CBRS data switching) and
* 2. A was validated before the switch
*
* The goal of this is to minimize the flickering in the UI of the cellular indicator
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index ac4d55c..08c14e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import kotlinx.coroutines.flow.StateFlow
/** Provides data related to the wifi state. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 2cb81c8..e0e0ed7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -23,9 +23,9 @@
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index a19c3c3e..a4fbc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -19,9 +19,9 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
index 5d4a666..86a668a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
@@ -18,8 +18,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index c45b420..7b486c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -43,9 +43,9 @@
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 86dcd18..96ab074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -21,8 +21,8 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -58,25 +58,29 @@
}
@SysUISingleton
-class WifiInteractorImpl @Inject constructor(
+class WifiInteractorImpl
+@Inject
+constructor(
connectivityRepository: ConnectivityRepository,
wifiRepository: WifiRepository,
) : WifiInteractor {
- override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
- when (info) {
- is WifiNetworkModel.Unavailable -> null
- is WifiNetworkModel.Invalid -> null
- is WifiNetworkModel.Inactive -> null
- is WifiNetworkModel.CarrierMerged -> null
- is WifiNetworkModel.Active -> when {
- info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
- info.passpointProviderFriendlyName
- info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
- else -> null
+ override val ssid: Flow<String?> =
+ wifiRepository.wifiNetwork.map { info ->
+ when (info) {
+ is WifiNetworkModel.Unavailable -> null
+ is WifiNetworkModel.Invalid -> null
+ is WifiNetworkModel.Inactive -> null
+ is WifiNetworkModel.CarrierMerged -> null
+ is WifiNetworkModel.Active ->
+ when {
+ info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
+ info.passpointProviderFriendlyName
+ info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
+ else -> null
+ }
}
}
- }
override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
@@ -86,7 +90,6 @@
override val activity: StateFlow<DataActivityModel> = wifiRepository.wifiActivity
- override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
- it.contains(ConnectivitySlot.WIFI)
- }
+ override val isForceHidden: Flow<Boolean> =
+ connectivityRepository.forceHiddenSlots.map { it.contains(ConnectivitySlot.WIFI) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
index da2daf2..0923d78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.wifi.data.model
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
-import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import android.telephony.SubscriptionManager
import androidx.annotation.VisibleForTesting
-import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.log.table.Diffable
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
+import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
/** Provides information about the current wifi network. */
sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
@@ -57,9 +57,7 @@
}
}
- /**
- * A model representing that the wifi information we received was invalid in some way.
- */
+ /** A model representing that the wifi information we received was invalid in some way. */
data class Invalid(
/** A description of why the wifi information was invalid. */
val invalidReason: String,
@@ -142,21 +140,17 @@
*/
val subscriptionId: Int,
- /**
- * The signal level, guaranteed to be 0 <= level <= numberOfLevels.
- */
+ /** The signal level, guaranteed to be 0 <= level <= numberOfLevels. */
val level: Int,
- /**
- * The maximum possible level.
- */
- val numberOfLevels: Int = DEFAULT_NUM_LEVELS,
+ /** The maximum possible level. */
+ val numberOfLevels: Int = MobileConnectionRepository.DEFAULT_NUM_LEVELS,
) : WifiNetworkModel() {
init {
require(level in MIN_VALID_LEVEL..numberOfLevels) {
"0 <= wifi level <= $numberOfLevels required; level was $level"
}
- require(subscriptionId != INVALID_SUBSCRIPTION_ID) {
+ require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
"subscription ID cannot be invalid"
}
}
@@ -208,9 +202,7 @@
/** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */
val isValidated: Boolean = false,
- /**
- * The wifi signal level, guaranteed to be 0 <= level <= 4.
- */
+ /** The wifi signal level, guaranteed to be 0 <= level <= 4. */
val level: Int,
/** See [android.net.wifi.WifiInfo.ssid]. */
@@ -255,8 +247,10 @@
if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
}
- if (prevVal.isOnlineSignUpForPasspointAccessPoint !=
- isOnlineSignUpForPasspointAccessPoint) {
+ if (
+ prevVal.isOnlineSignUpForPasspointAccessPoint !=
+ isOnlineSignUpForPasspointAccessPoint
+ ) {
row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
}
if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
@@ -281,29 +275,29 @@
// Only include the passpoint-related values in the string if we have them. (Most
// networks won't have them so they'll be mostly clutter.)
val passpointString =
- if (isPasspointAccessPoint ||
- isOnlineSignUpForPasspointAccessPoint ||
- passpointProviderFriendlyName != null) {
+ if (
+ isPasspointAccessPoint ||
+ isOnlineSignUpForPasspointAccessPoint ||
+ passpointProviderFriendlyName != null
+ ) {
", isPasspointAp=$isPasspointAccessPoint, " +
"isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " +
"passpointName=$passpointProviderFriendlyName"
- } else {
- ""
- }
+ } else {
+ ""
+ }
return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " +
"level=$level, ssid=$ssid$passpointString)"
}
companion object {
- @VisibleForTesting
- internal const val MAX_VALID_LEVEL = 4
+ @VisibleForTesting internal const val MAX_VALID_LEVEL = 4
}
}
companion object {
- @VisibleForTesting
- internal const val MIN_VALID_LEVEL = 0
+ @VisibleForTesting internal const val MIN_VALID_LEVEL = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 95431af..0f5ff91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -25,8 +25,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
@@ -34,13 +32,15 @@
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -55,15 +55,12 @@
/**
* Models the UI state for the status bar wifi icon.
*
- * This class exposes three view models, one per status bar location:
- * - [home]
- * - [keyguard]
- * - [qs]
- * In order to get the UI state for the wifi icon, you must use one of those view models (whichever
- * is correct for your location).
+ * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs].
+ * In order to get the UI state for the wifi icon, you must use one of those view models (whichever
+ * is correct for your location).
*
- * Internally, this class maintains the current state of the wifi icon and notifies those three
- * view models of any changes.
+ * Internally, this class maintains the current state of the wifi icon and notifies those three view
+ * models of any changes.
*/
@SysUISingleton
class WifiViewModel
@@ -85,12 +82,13 @@
is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
is WifiNetworkModel.Invalid -> WifiIcon.Hidden
is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
- is WifiNetworkModel.Inactive -> WifiIcon.Visible(
- res = WIFI_NO_NETWORK,
- ContentDescription.Loaded(
- "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+ is WifiNetworkModel.Inactive ->
+ WifiIcon.Visible(
+ res = WIFI_NO_NETWORK,
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+ )
)
- )
is WifiNetworkModel.Active -> {
val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
when {
@@ -114,25 +112,25 @@
/** The wifi icon that should be displayed. */
private val wifiIcon: StateFlow<WifiIcon> =
combine(
- interactor.isEnabled,
- interactor.isDefault,
- interactor.isForceHidden,
- interactor.wifiNetwork,
- ) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
- if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
- return@combine WifiIcon.Hidden
- }
+ interactor.isEnabled,
+ interactor.isDefault,
+ interactor.isForceHidden,
+ interactor.wifiNetwork,
+ ) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
+ if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
+ return@combine WifiIcon.Hidden
+ }
- val icon = wifiNetwork.icon()
+ val icon = wifiNetwork.icon()
- return@combine when {
- isDefault -> icon
- wifiConstants.alwaysShowIconIfEnabled -> icon
- !connectivityConstants.hasDataCapabilities -> icon
- wifiNetwork is WifiNetworkModel.Active && wifiNetwork.isValidated -> icon
- else -> WifiIcon.Hidden
+ return@combine when {
+ isDefault -> icon
+ wifiConstants.alwaysShowIconIfEnabled -> icon
+ !connectivityConstants.hasDataCapabilities -> icon
+ wifiNetwork is WifiNetworkModel.Active && wifiNetwork.isValidated -> icon
+ else -> WifiIcon.Hidden
+ }
}
- }
.logDiffsForTable(
wifiTableLogBuffer,
columnPrefix = "",
@@ -147,34 +145,34 @@
/** The wifi activity status. Null if we shouldn't display the activity status. */
private val activity: Flow<DataActivityModel?> =
if (!connectivityConstants.shouldShowActivityConfig) {
- flowOf(null)
- } else {
- combine(interactor.activity, interactor.ssid) { activity, ssid ->
- when (ssid) {
- null -> null
- else -> activity
+ flowOf(null)
+ } else {
+ combine(interactor.activity, interactor.ssid) { activity, ssid ->
+ when (ssid) {
+ null -> null
+ else -> activity
+ }
}
}
- }
- .distinctUntilChanged()
- .logOutputChange(logger, "activity")
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+ .distinctUntilChanged()
+ .logOutputChange(logger, "activity")
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
private val isActivityInViewVisible: Flow<Boolean> =
- activity
- .map { it?.hasActivityIn == true }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ activity
+ .map { it?.hasActivityIn == true }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
private val isActivityOutViewVisible: Flow<Boolean> =
- activity
- .map { it?.hasActivityOut == true }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ activity
+ .map { it?.hasActivityOut == true }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
private val isActivityContainerVisible: Flow<Boolean> =
- combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
- activityIn || activityOut
- }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+ combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
+ activityIn || activityOut
+ }
+ .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
// TODO(b/238425913): It isn't ideal for the wifi icon to need to know about whether the
// airplane icon is visible. Instead, we should have a parent StatusBarSystemIconsViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
new file mode 100644
index 0000000..2a18b81
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use mHost 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.systemui.statusbar.policy
+
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.AlarmTile
+import com.android.systemui.qs.tiles.CameraToggleTile
+import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.LocationTile
+import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.UiModeNightTile
+import com.android.systemui.qs.tiles.WorkModeTile
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface PolicyModule {
+
+ /** Inject DndTile into tileMap in QSModule */
+ @Binds @IntoMap @StringKey(DndTile.TILE_SPEC) fun bindDndTile(dndTile: DndTile): QSTileImpl<*>
+
+ /** Inject WorkModeTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(WorkModeTile.TILE_SPEC)
+ fun bindWorkModeTile(workModeTile: WorkModeTile): QSTileImpl<*>
+
+ /** Inject FlashlightTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(FlashlightTile.TILE_SPEC)
+ fun bindFlashlightTile(flashlightTile: FlashlightTile): QSTileImpl<*>
+
+ /** Inject LocationTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(LocationTile.TILE_SPEC)
+ fun bindLocationTile(locationTile: LocationTile): QSTileImpl<*>
+
+ /** Inject CameraToggleTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(CameraToggleTile.TILE_SPEC)
+ fun bindCameraToggleTile(cameraToggleTile: CameraToggleTile): QSTileImpl<*>
+
+ /** Inject MicrophoneToggleTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(MicrophoneToggleTile.TILE_SPEC)
+ fun bindMicrophoneToggleTile(microphoneToggleTile: MicrophoneToggleTile): QSTileImpl<*>
+
+ /** Inject AlarmTile into tileMap in QSModule */
+ @Binds
+ @IntoMap
+ @StringKey(AlarmTile.TILE_SPEC)
+ fun bindAlarmTile(alarmTile: AlarmTile): QSTileImpl<*>
+
+ @Binds
+ @IntoMap
+ @StringKey(UiModeNightTile.TILE_SPEC)
+ fun bindUiModeNightTile(uiModeNightTile: UiModeNightTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 2b29885..f7c8bac 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -21,6 +21,7 @@
import com.android.settingslib.users.EditUserInfoController;
import com.android.systemui.user.data.repository.UserRepositoryModule;
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
import com.android.systemui.user.ui.dialog.UserDialogModule;
import dagger.Binds;
@@ -36,6 +37,7 @@
includes = {
UserDialogModule.class,
UserRepositoryModule.class,
+ HeadlessSystemUserModeModule.class,
}
)
public abstract class UserModule {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
new file mode 100644
index 0000000..756e6a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.systemui.user.domain.interactor
+
+import android.os.UserManager
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface HeadlessSystemUserMode {
+
+ fun isHeadlessSystemUserMode(): Boolean
+}
+
+@SysUISingleton
+class HeadlessSystemUserModeImpl @Inject constructor() : HeadlessSystemUserMode {
+ override fun isHeadlessSystemUserMode(): Boolean {
+ return UserManager.isHeadlessSystemUserMode()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
new file mode 100644
index 0000000..0efa2d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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.systemui.user.domain.interactor
+
+import dagger.Binds
+
+@dagger.Module
+interface HeadlessSystemUserModeModule {
+
+ @Binds
+ fun bindIsHeadlessSystemUserMode(impl: HeadlessSystemUserModeImpl): HeadlessSystemUserMode
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c0ba3cc..3f895ad 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -86,6 +86,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val featureFlags: FeatureFlags,
private val manager: UserManager,
+ private val headlessSystemUserMode: HeadlessSystemUserMode,
@Application private val applicationScope: CoroutineScope,
telephonyInteractor: TelephonyInteractor,
broadcastDispatcher: BroadcastDispatcher,
@@ -560,7 +561,10 @@
actionType = action,
isRestricted = isRestricted,
isSwitchToEnabled =
- canSwitchUsers(selectedUserId) &&
+ canSwitchUsers(
+ selectedUserId = selectedUserId,
+ isAction = true,
+ ) &&
// If the user is auto-created is must not be currently resetting.
!(isGuestUserAutoCreated && isGuestUserResetting),
)
@@ -712,10 +716,32 @@
}
}
- private suspend fun canSwitchUsers(selectedUserId: Int): Boolean {
- return withContext(backgroundDispatcher) {
- manager.getUserSwitchability(UserHandle.of(selectedUserId))
- } == UserManager.SWITCHABILITY_STATUS_OK
+ private suspend fun canSwitchUsers(
+ selectedUserId: Int,
+ isAction: Boolean = false,
+ ): Boolean {
+ val isHeadlessSystemUserMode =
+ withContext(backgroundDispatcher) { headlessSystemUserMode.isHeadlessSystemUserMode() }
+ // Whether menu item should be active. True if item is a user or if any user has
+ // signed in since reboot or in all cases for non-headless system user mode.
+ val isItemEnabled = !isAction || !isHeadlessSystemUserMode || isAnyUserUnlocked()
+ return isItemEnabled &&
+ withContext(backgroundDispatcher) {
+ manager.getUserSwitchability(UserHandle.of(selectedUserId))
+ } == UserManager.SWITCHABILITY_STATUS_OK
+ }
+
+ private suspend fun isAnyUserUnlocked(): Boolean {
+ return manager
+ .getUsers(
+ /* excludePartial= */ true,
+ /* excludeDying= */ true,
+ /* excludePreCreated= */ true
+ )
+ .any { user ->
+ user.id != UserHandle.USER_SYSTEM &&
+ withContext(backgroundDispatcher) { manager.isUserUnlocked(user.userHandle) }
+ }
}
@SuppressLint("UseCompatLoadingForDrawables")
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
index b41bca0..8d32a48 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/ConditionalCoreStartable.java
@@ -43,11 +43,6 @@
@Override
public final void start() {
- if (mConditionSet == null || mConditionSet.isEmpty()) {
- onStart();
- return;
- }
-
mStartToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
@@ -63,11 +58,6 @@
@Override
public final void onBootCompleted() {
- if (mConditionSet == null || mConditionSet.isEmpty()) {
- bootCompleted();
- return;
- }
-
mBootCompletedToken = mMonitor.addSubscription(
new Monitor.Subscription.Builder(allConditionsMet -> {
if (allConditionsMet) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
new file mode 100644
index 0000000..c74e71f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitorModule.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.systemui.util.leak
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface GarbageMonitorModule {
+ /** Inject into GarbageMonitor.Service. */
+ @Binds
+ @IntoMap
+ @ClassKey(GarbageMonitor::class)
+ fun bindGarbageMonitorService(sysui: GarbageMonitor.Service): CoreStartable
+
+ @Binds
+ @IntoMap
+ @StringKey(GarbageMonitor.MemoryTile.TILE_SPEC)
+ fun bindMemoryTile(memoryTile: GarbageMonitor.MemoryTile): QSTileImpl<*>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 2c901d2..9429d89 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -22,6 +22,8 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.QuickAccessWalletTile;
import com.android.systemui.wallet.ui.WalletActivity;
import java.util.concurrent.Executor;
@@ -31,6 +33,7 @@
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
+import dagger.multibindings.StringKey;
/**
@@ -52,4 +55,11 @@
@Background Executor bgExecutor) {
return QuickAccessWalletClient.create(context, bgExecutor);
}
+
+ /** */
+ @Binds
+ @IntoMap
+ @StringKey(QuickAccessWalletTile.TILE_SPEC)
+ public abstract QSTileImpl<?> bindQuickAccessWalletTile(
+ QuickAccessWalletTile quickAccessWalletTile);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index e8d50ca..badeb27 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -24,13 +24,19 @@
import android.os.PowerManager
import android.os.PowerManager.WAKE_REASON_BIOMETRIC
import android.os.UserHandle
-import android.provider.Settings
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.FakeSettings
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -41,20 +47,11 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.io.PrintWriter
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
- private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
- private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
- private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
- private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
- private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
- private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
- private val fakeWakeupsConsideredUnlockIntents =
- Uri.Builder().appendPath("wakeups-considered-unlock-intent").build()
-
- @Mock
- private lateinit var secureSettings: SecureSettings
+ private lateinit var secureSettings: FakeSettings
@Mock
private lateinit var contentResolver: ContentResolver
@Mock
@@ -63,33 +60,20 @@
private lateinit var dumpManager: DumpManager
@Mock
private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var mockPrintWriter: PrintWriter
@Captor
private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
private lateinit var activeUnlockConfig: ActiveUnlockConfig
+ private var currentUser: Int = 0
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE))
- .thenReturn(fakeWakeUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
- .thenReturn(fakeUnlockIntentUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
- .thenReturn(fakeBioFailUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
- .thenReturn(fakeFaceErrorsUri)
- `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
- .thenReturn(fakeFaceAcquiredUri)
- `when`(secureSettings.getUriFor(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
- .thenReturn(fakeUnlockIntentBioEnroll)
- `when`(secureSettings.getUriFor(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
- .thenReturn(fakeWakeupsConsideredUnlockIntents)
-
+ currentUser = KeyguardUpdateMonitor.getCurrentUser()
+ secureSettings = FakeSettings()
activeUnlockConfig = ActiveUnlockConfig(
handler,
secureSettings,
@@ -105,8 +89,6 @@
@Test
fun onWakeupSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -114,9 +96,8 @@
)
// WHEN unlock on wake is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
- 0, 0)).thenReturn(1)
- updateSetting(fakeWakeUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_WAKE, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
// THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
assertTrue(
@@ -135,8 +116,6 @@
@Test
fun onUnlockIntentSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled
assertFalse(
activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -144,9 +123,8 @@
)
// WHEN unlock on biometric failed is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
- 0, 0)).thenReturn(1)
- updateSetting(fakeUnlockIntentUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -159,21 +137,19 @@
@Test
fun onBioFailSettingChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN no active unlock settings enabled and triggering unlock intent on biometric
// enrollment setting is disabled (empty string is disabled, null would use the default)
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn("")
- updateSetting(fakeUnlockIntentBioEnroll)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, "", currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL))
// WHEN unlock on biometric failed is allowed
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -186,17 +162,14 @@
@Test
fun faceErrorSettingsChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face error timeout (3), allow trigger active unlock
- `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
- 0)).thenReturn("3")
- updateSetting(fakeFaceAcquiredUri)
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_FACE_ERRORS, "3", currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
// THEN active unlock triggers allowed on error TIMEOUT
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
@@ -208,19 +181,17 @@
@Test
fun faceAcquiredSettingsChanged() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, "1", currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
- `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
"${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
- "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
- updateSetting(fakeFaceAcquiredUri)
+ "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
// THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
@@ -236,23 +207,23 @@
@Test
fun triggerOnUnlockIntentWhenBiometricEnrolledNone() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are NOT enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
- `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+ `when`(keyguardUpdateMonitor.isFaceEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// WHEN unlock intent is allowed when NO biometrics are enrolled (0)
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn("${ActiveUnlockConfig.BiometricType.NONE.intValue}")
- updateSetting(fakeUnlockIntentBioEnroll)
+
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "${ActiveUnlockConfig.BiometricType.NONE.intValue}", currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
// THEN active unlock triggers allowed on unlock intent
assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -261,12 +232,9 @@
@Test
fun triggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
- verifyRegisterSettingObserver()
-
// GIVEN unlock on biometric fail
- `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
- 0, 0)).thenReturn(1)
- updateSetting(fakeBioFailUri)
+ secureSettings.putIntForUser(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 1, currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are both enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
@@ -275,12 +243,14 @@
// WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
// are enrolled
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
"${ActiveUnlockConfig.BiometricType.ANY_FACE.intValue}" +
- "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}")
- updateSetting(fakeUnlockIntentBioEnroll)
+ "|${ActiveUnlockConfig.BiometricType.ANY_FINGERPRINT.intValue}",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
// THEN active unlock triggers NOT allowed on unlock intent
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -305,13 +275,12 @@
@Test
fun isWakeupConsideredUnlockIntent_singleValue() {
- verifyRegisterSettingObserver()
-
// GIVEN lift is considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(PowerManager.WAKE_REASON_LIFT.toString())
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ secureSettings.putIntForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
+ PowerManager.WAKE_REASON_LIFT,
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN only WAKE_REASON_LIFT is considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -325,17 +294,15 @@
@Test
fun isWakeupConsideredUnlockIntent_multiValue() {
- verifyRegisterSettingObserver()
-
// GIVEN lift and tap are considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(
+ secureSettings.putStringForUser(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
PowerManager.WAKE_REASON_LIFT.toString() +
"|" +
- PowerManager.WAKE_REASON_TAP.toString()
+ PowerManager.WAKE_REASON_TAP.toString(),
+ currentUser
)
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN WAKE_REASON_LIFT and WAKE_REASON TAP are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -354,13 +321,10 @@
@Test
fun isWakeupConsideredUnlockIntent_emptyValues() {
- verifyRegisterSettingObserver()
-
// GIVEN lift and tap are considered an unlock intent
- `when`(secureSettings.getStringForUser(
- Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS,
- 0)).thenReturn(" ")
- updateSetting(fakeWakeupsConsideredUnlockIntents)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, " ",
+ currentUser)
+ updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS))
// THEN no wake up gestures are considered an unlock intent
for (wakeReason in 0..WAKE_REASON_BIOMETRIC) {
@@ -373,7 +337,23 @@
PowerManager.WAKE_REASON_UNFOLD_DEVICE))
}
+ @Test
+ fun dump_onUnlockIntentWhenBiometricEnrolled_invalidNum_noArrayOutOfBoundsException() {
+ // GIVEN an invalid input (-1)
+ secureSettings.putStringForUser(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+ "-1", currentUser)
+
+ // WHEN the setting updates
+ updateSetting(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
+
+ // THEN no exception thrown
+ activeUnlockConfig.dump(mockPrintWriter, emptyArray())
+ }
+
private fun updateSetting(uri: Uri) {
+ verifyRegisterSettingObserver()
settingsObserverCaptor.value.onChange(
false,
listOf(uri),
@@ -383,13 +363,17 @@
}
private fun verifyRegisterSettingObserver() {
- verifyRegisterSettingObserver(fakeWakeUri)
- verifyRegisterSettingObserver(fakeUnlockIntentUri)
- verifyRegisterSettingObserver(fakeBioFailUri)
- verifyRegisterSettingObserver(fakeFaceErrorsUri)
- verifyRegisterSettingObserver(fakeFaceAcquiredUri)
- verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
- verifyRegisterSettingObserver(fakeWakeupsConsideredUnlockIntents)
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
+ ))
+ verifyRegisterSettingObserver(secureSettings.getUriFor(
+ ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS
+ ))
}
private fun verifyRegisterSettingObserver(uri: Uri) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
new file mode 100644
index 0000000..777dd4e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 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.systemui.accessibility.fontscaling
+
+import android.os.Handler
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.widget.ImageView
+import android.widget.SeekBar
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SystemSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for [FontScalingDialog]. */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class FontScalingDialogTest : SysuiTestCase() {
+ private lateinit var fontScalingDialog: FontScalingDialog
+ private lateinit var systemSettings: SystemSettings
+ private val fontSizeValueArray: Array<String> =
+ mContext
+ .getResources()
+ .getStringArray(com.android.settingslib.R.array.entryvalues_font_size)
+
+ @Before
+ fun setUp() {
+ val mainHandler = Handler(TestableLooper.get(this).getLooper())
+ systemSettings = FakeSettings()
+ fontScalingDialog = FontScalingDialog(mContext, systemSettings as FakeSettings)
+ }
+
+ @Test
+ fun showTheDialog_seekbarIsShowingCorrectProgress() {
+ fontScalingDialog.show()
+
+ val seekBar: SeekBar = fontScalingDialog.findViewById<SeekBar>(R.id.seekbar)!!
+ val progress: Int = seekBar.getProgress()
+ val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+
+ assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat())
+
+ fontScalingDialog.dismiss()
+ }
+
+ @Test
+ fun progressIsZero_clickIconEnd_seekBarProgressIncreaseOne_fontSizeScaled() {
+ fontScalingDialog.show()
+
+ val iconEnd: ImageView = fontScalingDialog.findViewById(R.id.icon_end)!!
+ val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
+ fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
+ val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+
+ seekBarWithIconButtonsView.setProgress(0)
+
+ iconEnd.performClick()
+
+ val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+ assertThat(seekBar.getProgress()).isEqualTo(1)
+ assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat())
+
+ fontScalingDialog.dismiss()
+ }
+
+ @Test
+ fun progressIsMax_clickIconStart_seekBarProgressDecreaseOne_fontSizeScaled() {
+ fontScalingDialog.show()
+
+ val iconStart: ImageView = fontScalingDialog.findViewById(R.id.icon_start)!!
+ val seekBarWithIconButtonsView: SeekBarWithIconButtonsView =
+ fontScalingDialog.findViewById(R.id.font_scaling_slider)!!
+ val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!!
+
+ seekBarWithIconButtonsView.setProgress(fontSizeValueArray.size - 1)
+
+ iconStart.performClick()
+
+ val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def = */ 1.0f)
+ assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2)
+ assertThat(currentScale)
+ .isEqualTo(fontSizeValueArray[fontSizeValueArray.size - 2].toFloat())
+
+ fontScalingDialog.dismiss()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 9f4a7c8..b3329eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -32,7 +32,9 @@
import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -66,13 +68,16 @@
private ComplicationTypesUpdater mController;
+ private Monitor mMonitor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
+ mMonitor = SelfExecutingMonitor.createInstance();
mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
- mSecureSettings, mDreamOverlayStateController);
+ mSecureSettings, mDreamOverlayStateController, mMonitor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index ec448f9..f6662d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -29,7 +29,9 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
import org.junit.Before;
import org.junit.Test;
@@ -69,10 +71,13 @@
@Mock
private ComplicationLayoutParams mLayoutParams;
+ private Monitor mMonitor;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder);
+ mMonitor = SelfExecutingMonitor.createInstance();
}
/**
@@ -83,7 +88,8 @@
final DreamClockTimeComplication.Registrant registrant =
new DreamClockTimeComplication.Registrant(
mDreamOverlayStateController,
- mComplication);
+ mComplication,
+ mMonitor);
registrant.start();
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index 0e249b3..3312c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.view.LaunchableImageView;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.controller.ControlsController;
import com.android.systemui.controls.controller.StructureInfo;
@@ -46,6 +47,7 @@
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
import org.junit.Before;
import org.junit.Test;
@@ -101,6 +103,8 @@
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
+ private Monitor mMonitor;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -112,6 +116,8 @@
Optional.of(mControlsListingController));
when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
when(mView.findViewById(R.id.home_controls_chip)).thenReturn(mHomeControlsView);
+
+ mMonitor = SelfExecutingMonitor.createInstance();
}
@Test
@@ -126,7 +132,7 @@
public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setHaveFavorites(false);
@@ -139,7 +145,7 @@
public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setHaveFavorites(false);
@@ -152,7 +158,7 @@
public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setHaveFavorites(false);
@@ -165,7 +171,7 @@
public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setHaveFavorites(true);
@@ -178,7 +184,7 @@
public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setHaveFavorites(true);
@@ -191,7 +197,7 @@
public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent);
+ mDreamOverlayStateController, mControlsComponent, mMonitor);
registrant.start();
setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index c8b2b25..ef62abf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -30,9 +30,12 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.condition.SelfExecutingMonitor;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
import org.junit.Before;
import org.junit.Test;
@@ -43,6 +46,8 @@
import org.mockito.MockitoAnnotations;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -60,9 +65,14 @@
@Mock
private View mBcSmartspaceView;
+ private Monitor mMonitor;
+
+ private final Set<Condition> mPreconditions = new HashSet<>();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ mMonitor = SelfExecutingMonitor.createInstance();
}
/**
@@ -79,7 +89,8 @@
return new SmartSpaceComplication.Registrant(
mDreamOverlayStateController,
mComplication,
- mSmartspaceController);
+ mSmartspaceController,
+ mMonitor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
index db18ba6..5bb8367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfigTest.kt
@@ -18,15 +18,19 @@
package com.android.systemui.keyguard.data.quickaffordance
import android.app.StatusBarManager
+import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.pm.PackageManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.camera.CameraGestureHelper
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -44,21 +48,28 @@
@Mock private lateinit var cameraGestureHelper: CameraGestureHelper
@Mock private lateinit var context: Context
@Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: CameraQuickAffordanceConfig
+ private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- setLaunchable(true)
+ setLaunchable()
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
underTest =
CameraQuickAffordanceConfig(
context,
packageManager,
- ) {
- cameraGestureHelper
- }
+ { cameraGestureHelper },
+ userTracker,
+ devicePolicyManager,
+ testDispatcher,
+ )
}
@Test
@@ -73,23 +84,57 @@
}
@Test
- fun `getPickerScreenState - default when launchable`() = runTest {
- setLaunchable(true)
+ fun `getPickerScreenState - default when launchable`() =
+ testScope.runTest {
+ setLaunchable(true)
- Truth.assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
- }
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
@Test
- fun `getPickerScreenState - unavailable when not launchable`() = runTest {
- setLaunchable(false)
+ fun `getPickerScreenState - unavailable when camera app not installed`() =
+ testScope.runTest {
+ setLaunchable(isCameraAppInstalled = false)
- Truth.assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
- private fun setLaunchable(isLaunchable: Boolean) {
+ @Test
+ fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByDeviceAdmin = true)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when secure camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isSecureCameraDisabledByDeviceAdmin = true)
+
+ Truth.assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(
+ isCameraAppInstalled: Boolean = true,
+ isCameraDisabledByDeviceAdmin: Boolean = false,
+ isSecureCameraDisabledByDeviceAdmin: Boolean = false,
+ ) {
whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
- .thenReturn(isLaunchable)
+ .thenReturn(isCameraAppInstalled)
+ whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+ .thenReturn(isCameraDisabledByDeviceAdmin)
+ whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
+ .thenReturn(
+ if (isSecureCameraDisabledByDeviceAdmin) {
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
+ } else {
+ 0
+ }
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
index 5bd86bd..f1b9c5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfigTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
+import android.app.admin.DevicePolicyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
@@ -24,11 +25,14 @@
import com.android.systemui.camera.CameraIntentsWrapper
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -44,59 +48,94 @@
class VideoCameraQuickAffordanceConfigTest : SysuiTestCase() {
@Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+ @Mock private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var underTest: VideoCameraQuickAffordanceConfig
+ private lateinit var userTracker: UserTracker
+ private lateinit var testScope: TestScope
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ val testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+ userTracker = FakeUserTracker()
underTest =
VideoCameraQuickAffordanceConfig(
context = context,
cameraIntents = CameraIntentsWrapper(context),
activityIntentHelper = activityIntentHelper,
- userTracker = FakeUserTracker(),
+ userTracker = userTracker,
+ devicePolicyManager = devicePolicyManager,
+ backgroundDispatcher = testDispatcher,
)
}
@Test
- fun `lockScreenState - visible when launchable`() = runTest {
- setLaunchable(true)
+ fun `lockScreenState - visible when launchable`() =
+ testScope.runTest {
+ setLaunchable()
- val lockScreenState = collectLastValue(underTest.lockScreenState)
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
- assertThat(lockScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
- }
+ assertThat(lockScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java)
+ }
@Test
- fun `lockScreenState - hidden when not launchable`() = runTest {
- setLaunchable(false)
+ fun `lockScreenState - hidden when app not installed on device`() =
+ testScope.runTest {
+ setLaunchable(isVideoCameraAppInstalled = false)
- val lockScreenState = collectLastValue(underTest.lockScreenState)
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
- assertThat(lockScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
- }
+ assertThat(lockScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun `getPickerScreenState - default when launchable`() = runTest {
- setLaunchable(true)
+ fun `lockScreenState - hidden when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByAdmin = true)
- assertThat(underTest.getPickerScreenState())
- .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
- }
+ val lockScreenState = collectLastValue(underTest.lockScreenState)
+
+ assertThat(lockScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
@Test
- fun `getPickerScreenState - unavailable when not launchable`() = runTest {
- setLaunchable(false)
+ fun `getPickerScreenState - default when launchable`() =
+ testScope.runTest {
+ setLaunchable()
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java)
+ }
- private fun setLaunchable(isLaunchable: Boolean) {
+ @Test
+ fun `getPickerScreenState - unavailable when app not installed on device`() =
+ testScope.runTest {
+ setLaunchable(isVideoCameraAppInstalled = false)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ @Test
+ fun `getPickerScreenState - unavailable when camera disabled by admin`() =
+ testScope.runTest {
+ setLaunchable(isCameraDisabledByAdmin = true)
+
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
+
+ private fun setLaunchable(
+ isVideoCameraAppInstalled: Boolean = true,
+ isCameraDisabledByAdmin: Boolean = false,
+ ) {
whenever(
activityIntentHelper.getTargetActivityInfo(
any(),
@@ -105,11 +144,13 @@
)
)
.thenReturn(
- if (isLaunchable) {
+ if (isVideoCameraAppInstalled) {
mock()
} else {
null
}
)
+ whenever(devicePolicyManager.getCameraDisabled(null, userTracker.userId))
+ .thenReturn(isCameraDisabledByAdmin)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index 9f12329..a07a714 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -1897,6 +1897,20 @@
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
+ @Test
+ fun testSessionDestroyed_noNotificationKey_stillRemoved() {
+ whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+ whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+
+ // When a notiifcation is added and then removed before it is fully processed
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ backgroundExecutor.runAllReady()
+ mediaDataManager.onNotificationRemoved(KEY)
+
+ // We still make sure to remove it
+ verify(listener).onMediaDataRemoved(eq(KEY))
+ }
+
/** Helper function to add a media notification and capture the resulting MediaData */
private fun addNotificationAndLoad() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 997198e..a72634b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -61,7 +61,6 @@
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -69,6 +68,8 @@
import org.mockito.Mock
import org.mockito.Mockito.floatThat
import org.mockito.Mockito.mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -107,7 +108,6 @@
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
@Captor
lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
- @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
@Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
@Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -150,7 +150,6 @@
MediaPlayerData.clear()
}
- @Ignore("b/253229241")
@Test
fun testPlayerOrdering() {
// Test values: key, data, last active time
@@ -327,7 +326,6 @@
}
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized() {
testPlayerOrdering()
@@ -335,7 +333,7 @@
// If smartspace is prioritized
MediaPlayerData.addMediaRecommendation(
SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA,
+ EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
panel,
true,
clock
@@ -345,7 +343,6 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
testPlayerOrdering()
@@ -362,7 +359,6 @@
assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(2).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testOrderWithSmartspace_notPrioritized() {
testPlayerOrdering()
@@ -370,7 +366,7 @@
// If smartspace is not prioritized
MediaPlayerData.addMediaRecommendation(
SMARTSPACE_KEY,
- EMPTY_SMARTSPACE_MEDIA_DATA,
+ EMPTY_SMARTSPACE_MEDIA_DATA.copy(isActive = true),
panel,
false,
clock
@@ -381,7 +377,6 @@
assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
}
- @Ignore("b/253229241")
@Test
fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
testPlayerOrdering()
@@ -419,7 +414,6 @@
)
}
- @Ignore("b/253229241")
@Test
fun testSwipeDismiss_logged() {
mediaCarouselController.mediaCarouselScrollHandler.dismissCallback.invoke()
@@ -427,7 +421,6 @@
verify(logger).logSwipeDismiss()
}
- @Ignore("b/253229241")
@Test
fun testSettingsButton_logged() {
mediaCarouselController.settingsButton.callOnClick()
@@ -435,18 +428,16 @@
verify(logger).logCarouselSettings()
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeQs_logged() {
mediaCarouselController.onDesiredLocationChanged(
- MediaHierarchyManager.LOCATION_QS,
+ LOCATION_QS,
mediaHostState,
animate = false
)
- verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QS)
+ verify(logger).logCarouselPosition(LOCATION_QS)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeQqs_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -457,7 +448,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_QQS)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeLockscreen_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -468,7 +458,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
- @Ignore("b/253229241")
@Test
fun testLocationChangeDream_logged() {
mediaCarouselController.onDesiredLocationChanged(
@@ -479,7 +468,6 @@
verify(logger).logCarouselPosition(MediaHierarchyManager.LOCATION_DREAM_OVERLAY)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemoved_logged() {
val packageName = "smartspace package"
@@ -493,7 +481,6 @@
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
- @Ignore("b/253229241")
@Test
fun testMediaLoaded_ScrollToActivePlayer() {
listener.value.onMediaDataLoaded(
@@ -551,7 +538,6 @@
)
}
- @Ignore("b/253229241")
@Test
fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
listener.value.onSmartspaceMediaDataLoaded(
@@ -595,7 +581,6 @@
assertEquals(playerIndex, 0)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
var result = false
@@ -607,7 +592,6 @@
assertEquals(true, result)
}
- @Ignore("b/253229241")
@Test
fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
var result = false
@@ -621,7 +605,6 @@
assertEquals(true, result)
}
- @Ignore("b/253229241")
@Test
fun testGetCurrentVisibleMediaContentIntent() {
val clickIntent1 = mock(PendingIntent::class.java)
@@ -668,7 +651,6 @@
assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
}
- @Ignore("b/253229241")
@Test
fun testSetCurrentState_UpdatePageIndicatorAlphaWhenSquish() {
val delta = 0.0001F
@@ -690,7 +672,6 @@
verify(pageIndicator).alpha = floatThat { abs(it - 1.0F) < delta }
}
- @Ignore("b/253229241")
@Test
fun testOnConfigChanged_playersAreAddedBack() {
listener.value.onMediaDataLoaded(
@@ -716,7 +697,7 @@
val playersSize = MediaPlayerData.players().size
- configListener.value.onConfigChanged(capture(newConfig))
+ configListener.value.onConfigChanged(Configuration())
assertEquals(playersSize, MediaPlayerData.players().size)
assertEquals(
@@ -796,4 +777,59 @@
job.cancel()
}
+
+ @Test
+ fun testInvisibleToUserAndExpanded_playersNotListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel visible to user in expanded layout.
+ mediaCarouselController.currentlyExpanded = true
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to true.
+ verify(panel, times(MediaPlayerData.players().size)).listening = true
+
+ // Make the carousel invisible to user.
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = false
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+ }
+
+ @Test
+ fun testVisibleToUserAndExpanded_playersListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel visible to user in expanded layout.
+ mediaCarouselController.currentlyExpanded = true
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to true.
+ verify(panel, times(MediaPlayerData.players().size)).listening = true
+ }
+
+ @Test
+ fun testUMOCollapsed_playersNotListening() {
+ // Add players to carousel.
+ testPlayerOrdering()
+
+ // Make the carousel in collapsed layout.
+ mediaCarouselController.currentlyExpanded = false
+
+ // panel is the player for each MediaPlayerData.
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+
+ // Make the carousel visible to user.
+ reset(panel)
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = true
+
+ // Verify that seekbar listening attribute in media control panel is set to false.
+ verify(panel, times(MediaPlayerData.players().size)).listening = false
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 4635b48..e222542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -52,14 +52,15 @@
import com.android.systemui.qs.tiles.WorkModeTile
import com.android.systemui.util.leak.GarbageMonitor
import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.Mock
import org.mockito.Mockito.inOrder
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
private val specMap = mapOf(
"internet" to InternetTile::class.java,
@@ -140,40 +141,43 @@
whenever(qsHost.userContext).thenReturn(mContext)
whenever(customTileBuilder.build()).thenReturn(customTile)
+ val tileMap = mutableMapOf<String, Provider<QSTileImpl<*>>>(
+ "internet" to Provider { internetTile },
+ "bt" to Provider { bluetoothTile },
+ "dnd" to Provider { dndTile },
+ "inversion" to Provider { colorInversionTile },
+ "airplane" to Provider { airplaneTile },
+ "work" to Provider { workTile },
+ "rotation" to Provider { rotationTile },
+ "flashlight" to Provider { flashlightTile },
+ "location" to Provider { locationTile },
+ "cast" to Provider { castTile },
+ "hotspot" to Provider { hotspotTile },
+ "battery" to Provider { batterySaverTile },
+ "saver" to Provider { dataSaverTile },
+ "night" to Provider { nightDisplayTile },
+ "nfc" to Provider { nfcTile },
+ "dark" to Provider { darkModeTile },
+ "screenrecord" to Provider { screenRecordTile },
+ "reduce_brightness" to Provider { reduceBrightColorsTile },
+ "cameratoggle" to Provider { cameraToggleTile },
+ "mictoggle" to Provider { microphoneToggleTile },
+ "controls" to Provider { deviceControlsTile },
+ "alarm" to Provider { alarmTile },
+ "wallet" to Provider { quickAccessWalletTile },
+ "qr_code_scanner" to Provider { qrCodeScannerTile },
+ "onehanded" to Provider { oneHandedModeTile },
+ "color_correction" to Provider { colorCorrectionTile },
+ "dream" to Provider { dreamTile },
+ "font_scaling" to Provider { fontScalingTile }
+ )
+
factory = QSFactoryImpl(
{ qsHost },
{ customTileBuilder },
- { internetTile },
- { bluetoothTile },
- { dndTile },
- { colorInversionTile },
- { airplaneTile },
- { workTile },
- { rotationTile },
- { flashlightTile },
- { locationTile },
- { castTile },
- { hotspotTile },
- { batterySaverTile },
- { dataSaverTile },
- { nightDisplayTile },
- { nfcTile },
- { memoryTile },
- { darkModeTile },
- { screenRecordTile },
- { reduceBrightColorsTile },
- { cameraToggleTile },
- { microphoneToggleTile },
- { deviceControlsTile },
- { alarmTile },
- { quickAccessWalletTile },
- { qrCodeScannerTile },
- { oneHandedModeTile },
- { colorCorrectionTile },
- { dreamTile },
- { fontScalingTile }
+ tileMap,
)
- // When adding/removing tiles, fix also [specMap]
+ // When adding/removing tiles, fix also [specMap] and [tileMap]
}
@Test
@@ -209,4 +213,4 @@
inOrder.verify(tile).initialize()
inOrder.verify(tile).postStale()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/FontScalingTileTest.kt
new file mode 100644
index 0000000..57abae0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/FontScalingTileTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.systemui.qs.tiles.dialog
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class FontScalingTileTest : SysuiTestCase() {
+ @Mock private lateinit var qsHost: QSTileHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var fontScalingTile: FontScalingTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ `when`(qsHost.getContext()).thenReturn(mContext)
+ fontScalingTile =
+ FontScalingTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ dialogLaunchAnimator,
+ FakeSettings()
+ )
+ fontScalingTile.initialize()
+ }
+
+ @Test
+ fun isNotAvailable_whenNotSupportedDevice_returnsFalse() {
+ val isAvailable = fontScalingTile.isAvailable()
+
+ assertThat(isAvailable).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index 9eccbb6..aa1636d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
@@ -249,6 +249,21 @@
}
@Test
+ public void addCallback_preCondition_noConditions_reportAllConditionsMet() {
+ final Monitor
+ monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(mCondition1)));
+ final Monitor.Callback callback = mock(
+ Monitor.Callback.class);
+
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
+ mExecutor.runAllReady();
+ verify(callback, never()).onConditionsChanged(true);
+ mCondition1.fakeUpdateCondition(true);
+ mExecutor.runAllReady();
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
public void removeCallback_noFailureOnDoubleRemove() {
final Condition condition = mock(
Condition.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index abb4561..f0f213b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -26,8 +26,8 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index c02ca01..cd4d847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -32,8 +32,8 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 673e559..8090205 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -44,8 +44,8 @@
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
@@ -896,21 +896,31 @@
// Subscription 1
private const val SUB_1_ID = 1
+ private val GROUP_1 = ParcelUuid(UUID.randomUUID())
private val SUB_1 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
- whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ whenever(it.groupUuid).thenReturn(GROUP_1)
}
- private val MODEL_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
+ private val MODEL_1 =
+ SubscriptionModel(
+ subscriptionId = SUB_1_ID,
+ groupUuid = GROUP_1,
+ )
// Subscription 2
private const val SUB_2_ID = 2
+ private val GROUP_2 = ParcelUuid(UUID.randomUUID())
private val SUB_2 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
- whenever(it.groupUuid).thenReturn(ParcelUuid(UUID.randomUUID()))
+ whenever(it.groupUuid).thenReturn(GROUP_2)
}
- private val MODEL_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
+ private val MODEL_2 =
+ SubscriptionModel(
+ subscriptionId = SUB_2_ID,
+ groupUuid = GROUP_2,
+ )
// Subs 3 and 4 are considered to be in the same group ------------------------------------
private val GROUP_ID_3_4 = ParcelUuid(UUID.randomUUID())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index f8a9783..bbca001 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
+import android.os.ParcelUuid
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
@@ -34,6 +35,7 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -104,6 +106,21 @@
job.cancel()
}
+ // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
+ @Test
+ fun filteredSubscriptions_moreThanTwo_doesNotFilter() =
+ testScope.runTest {
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
+
+ job.cancel()
+ }
+
@Test
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
testScope.runTest {
@@ -118,10 +135,50 @@
}
@Test
- fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
+ fun filteredSubscriptions_opportunistic_differentGroups_doesNotFilter() =
testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
+
+ job.cancel()
+ }
+
+ @Test
+ fun filteredSubscriptions_opportunistic_nonGrouped_doesNotFilter() =
+ testScope.runTest {
+ val (sub1, sub2) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_2_ID),
+ opportunistic = Pair(true, true),
+ grouped = false,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub2))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
+
+ var latest: List<SubscriptionModel>? = null
+ val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(listOf(sub1, sub2))
+
+ job.cancel()
+ }
+
+ @Test
+ fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_3() =
+ testScope.runTest {
+ val (sub3, sub4) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+ opportunistic = Pair(true, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub3, sub4))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -129,15 +186,21 @@
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
- assertThat(latest).isEqualTo(listOf(SUB_3_OPP))
+ assertThat(latest).isEqualTo(listOf(sub3))
job.cancel()
}
@Test
- fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
+ fun filteredSubscriptions_opportunistic_grouped_configFalse_showsActive_4() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
+ val (sub3, sub4) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+ opportunistic = Pair(true, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub3, sub4))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
@@ -146,15 +209,21 @@
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
// Filtered subscriptions should show the active one when the config is false
- assertThat(latest).isEqualTo(listOf(SUB_4_OPP))
+ assertThat(latest).isEqualTo(listOf(sub4))
job.cancel()
}
@Test
- fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
+ fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_active_1() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+ val (sub1, sub3) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+ opportunistic = Pair(false, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
@@ -164,15 +233,21 @@
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
- assertThat(latest).isEqualTo(listOf(SUB_1))
+ assertThat(latest).isEqualTo(listOf(sub1))
job.cancel()
}
@Test
- fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
+ fun filteredSubscriptions_oneOpportunistic_grouped_configTrue_showsPrimary_nonActive_1() =
testScope.runTest {
- connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
+ val (sub1, sub3) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_1_ID, SUB_3_ID),
+ opportunistic = Pair(false, true),
+ grouped = true,
+ )
+ connectionsRepository.setSubscriptions(listOf(sub1, sub3))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
@@ -182,7 +257,7 @@
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
- assertThat(latest).isEqualTo(listOf(SUB_1))
+ assertThat(latest).isEqualTo(listOf(sub1))
job.cancel()
}
@@ -642,6 +717,33 @@
job.cancel()
}
+ /**
+ * Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
+ * flow.
+ */
+ private fun createSubscriptionPair(
+ subscriptionIds: Pair<Int, Int>,
+ opportunistic: Pair<Boolean, Boolean> = Pair(false, false),
+ grouped: Boolean = false,
+ ): Pair<SubscriptionModel, SubscriptionModel> {
+ val groupUuid = if (grouped) ParcelUuid(UUID.randomUUID()) else null
+ val sub1 =
+ SubscriptionModel(
+ subscriptionId = subscriptionIds.first,
+ isOpportunistic = opportunistic.first,
+ groupUuid = groupUuid,
+ )
+
+ val sub2 =
+ SubscriptionModel(
+ subscriptionId = subscriptionIds.second,
+ isOpportunistic = opportunistic.second,
+ groupUuid = groupUuid,
+ )
+
+ return Pair(sub1, sub2)
+ }
+
companion object {
private val tableLogBuffer =
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
@@ -655,11 +757,21 @@
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
private const val SUB_3_ID = 3
- private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
+ private val SUB_3_OPP =
+ SubscriptionModel(
+ subscriptionId = SUB_3_ID,
+ isOpportunistic = true,
+ groupUuid = ParcelUuid(UUID.randomUUID()),
+ )
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
private const val SUB_4_ID = 4
- private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
+ private val SUB_4_OPP =
+ SubscriptionModel(
+ subscriptionId = SUB_4_ID,
+ isOpportunistic = true,
+ groupUuid = ParcelUuid(UUID.randomUUID()),
+ )
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index f5837d6..1bf431b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
index 3c4e85b..9cf08c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepositoryTest.kt
@@ -19,7 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 7099f1f..db791bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -36,8 +36,8 @@
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -83,13 +83,14 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
+ broadcastDispatcher.broadcastFlow(
+ any(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
)
- ).thenReturn(flowOf(Unit))
+ .thenReturn(flowOf(Unit))
executor = FakeExecutor(FakeSystemClock())
scope = CoroutineScope(IMMEDIATE)
underTest = createRepo()
@@ -101,150 +102,152 @@
}
@Test
- fun isWifiEnabled_initiallyGetsWifiManagerValue() = runBlocking(IMMEDIATE) {
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ fun isWifiEnabled_initiallyGetsWifiManagerValue() =
+ runBlocking(IMMEDIATE) {
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
- underTest = createRepo()
+ underTest = createRepo()
- assertThat(underTest.isWifiEnabled.value).isTrue()
- }
-
- @Test
- fun isWifiEnabled_networkCapabilitiesChanged_valueUpdated() = runBlocking(IMMEDIATE) {
- // We need to call launch on the flows so that they start updating
- val networkJob = underTest.wifiNetwork.launchIn(this)
- val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
- )
-
- assertThat(underTest.isWifiEnabled.value).isTrue()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(false)
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
- )
-
- assertThat(underTest.isWifiEnabled.value).isFalse()
-
- networkJob.cancel()
- enabledJob.cancel()
- }
-
- @Test
- fun isWifiEnabled_networkLost_valueUpdated() = runBlocking(IMMEDIATE) {
- // We need to call launch on the flows so that they start updating
- val networkJob = underTest.wifiNetwork.launchIn(this)
- val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
- getNetworkCallback().onLost(NETWORK)
-
- assertThat(underTest.isWifiEnabled.value).isTrue()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(false)
- getNetworkCallback().onLost(NETWORK)
-
- assertThat(underTest.isWifiEnabled.value).isFalse()
-
- networkJob.cancel()
- enabledJob.cancel()
- }
-
- @Test
- fun isWifiEnabled_intentsReceived_valueUpdated() = runBlocking(IMMEDIATE) {
- val intentFlow = MutableSharedFlow<Unit>()
- whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
- )
- ).thenReturn(intentFlow)
- underTest = createRepo()
-
- val job = underTest.isWifiEnabled.launchIn(this)
-
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
- intentFlow.emit(Unit)
-
- assertThat(underTest.isWifiEnabled.value).isTrue()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(false)
- intentFlow.emit(Unit)
-
- assertThat(underTest.isWifiEnabled.value).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() = runBlocking(IMMEDIATE) {
- val intentFlow = MutableSharedFlow<Unit>()
- whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
- )
- ).thenReturn(intentFlow)
- underTest = createRepo()
-
- val networkJob = underTest.wifiNetwork.launchIn(this)
- val enabledJob = underTest.isWifiEnabled.launchIn(this)
-
- whenever(wifiManager.isWifiEnabled).thenReturn(false)
- intentFlow.emit(Unit)
- assertThat(underTest.isWifiEnabled.value).isFalse()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
- getNetworkCallback().onLost(NETWORK)
- assertThat(underTest.isWifiEnabled.value).isTrue()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(false)
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
- )
- assertThat(underTest.isWifiEnabled.value).isFalse()
-
- whenever(wifiManager.isWifiEnabled).thenReturn(true)
- intentFlow.emit(Unit)
- assertThat(underTest.isWifiEnabled.value).isTrue()
-
- networkJob.cancel()
- enabledJob.cancel()
- }
-
- @Test
- fun isWifiDefault_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
- val job = underTest.isWifiDefault.launchIn(this)
-
- assertThat(underTest.isWifiDefault.value).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun isWifiDefault_wifiNetwork_isTrue() = runBlocking(IMMEDIATE) {
- val job = underTest.isWifiDefault.launchIn(this)
-
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
+ assertThat(underTest.isWifiEnabled.value).isTrue()
}
- getDefaultNetworkCallback().onCapabilitiesChanged(
- NETWORK,
- createWifiNetworkCapabilities(wifiInfo)
- )
+ @Test
+ fun isWifiEnabled_networkCapabilitiesChanged_valueUpdated() =
+ runBlocking(IMMEDIATE) {
+ // We need to call launch on the flows so that they start updating
+ val networkJob = underTest.wifiNetwork.launchIn(this)
+ val enabledJob = underTest.isWifiEnabled.launchIn(this)
- assertThat(underTest.isWifiDefault.value).isTrue()
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- job.cancel()
- }
+ assertThat(underTest.isWifiEnabled.value).isTrue()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(false)
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+ assertThat(underTest.isWifiEnabled.value).isFalse()
+
+ networkJob.cancel()
+ enabledJob.cancel()
+ }
+
+ @Test
+ fun isWifiEnabled_networkLost_valueUpdated() =
+ runBlocking(IMMEDIATE) {
+ // We need to call launch on the flows so that they start updating
+ val networkJob = underTest.wifiNetwork.launchIn(this)
+ val enabledJob = underTest.isWifiEnabled.launchIn(this)
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ getNetworkCallback().onLost(NETWORK)
+
+ assertThat(underTest.isWifiEnabled.value).isTrue()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(false)
+ getNetworkCallback().onLost(NETWORK)
+
+ assertThat(underTest.isWifiEnabled.value).isFalse()
+
+ networkJob.cancel()
+ enabledJob.cancel()
+ }
+
+ @Test
+ fun isWifiEnabled_intentsReceived_valueUpdated() =
+ runBlocking(IMMEDIATE) {
+ val intentFlow = MutableSharedFlow<Unit>()
+ whenever(
+ broadcastDispatcher.broadcastFlow(
+ any(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
+ )
+ .thenReturn(intentFlow)
+ underTest = createRepo()
+
+ val job = underTest.isWifiEnabled.launchIn(this)
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ intentFlow.emit(Unit)
+
+ assertThat(underTest.isWifiEnabled.value).isTrue()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(false)
+ intentFlow.emit(Unit)
+
+ assertThat(underTest.isWifiEnabled.value).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() =
+ runBlocking(IMMEDIATE) {
+ val intentFlow = MutableSharedFlow<Unit>()
+ whenever(
+ broadcastDispatcher.broadcastFlow(
+ any(),
+ nullable(),
+ anyInt(),
+ nullable(),
+ )
+ )
+ .thenReturn(intentFlow)
+ underTest = createRepo()
+
+ val networkJob = underTest.wifiNetwork.launchIn(this)
+ val enabledJob = underTest.isWifiEnabled.launchIn(this)
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(false)
+ intentFlow.emit(Unit)
+ assertThat(underTest.isWifiEnabled.value).isFalse()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ getNetworkCallback().onLost(NETWORK)
+ assertThat(underTest.isWifiEnabled.value).isTrue()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(false)
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat(underTest.isWifiEnabled.value).isFalse()
+
+ whenever(wifiManager.isWifiEnabled).thenReturn(true)
+ intentFlow.emit(Unit)
+ assertThat(underTest.isWifiEnabled.value).isTrue()
+
+ networkJob.cancel()
+ enabledJob.cancel()
+ }
+
+ @Test
+ fun isWifiDefault_initiallyGetsDefault() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
+
+ assertThat(underTest.isWifiDefault.value).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun isWifiDefault_wifiNetwork_isTrue() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
+
+ val wifiInfo = mock<WifiInfo>().apply { whenever(this.ssid).thenReturn(SSID) }
+
+ getDefaultNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(underTest.isWifiDefault.value).isTrue()
+
+ job.cancel()
+ }
/** Regression test for b/266628069. */
@Test
@@ -252,16 +255,18 @@
runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
- val transportInfo = VpnTransportInfo(
- /* type= */ 0,
- /* sessionId= */ "sessionId",
- )
- val networkCapabilities = mock<NetworkCapabilities>().also {
- whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
- whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
- whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
- whenever(it.transportInfo).thenReturn(transportInfo)
- }
+ val transportInfo =
+ VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
+ val networkCapabilities =
+ mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(false)
+ whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+ whenever(it.transportInfo).thenReturn(transportInfo)
+ }
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
@@ -276,16 +281,18 @@
runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
- val transportInfo = VpnTransportInfo(
- /* type= */ 0,
- /* sessionId= */ "sessionId",
- )
- val networkCapabilities = mock<NetworkCapabilities>().also {
- whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
- whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
- whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
- whenever(it.transportInfo).thenReturn(transportInfo)
- }
+ val transportInfo =
+ VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
+ val networkCapabilities =
+ mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+ whenever(it.transportInfo).thenReturn(transportInfo)
+ }
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, networkCapabilities)
@@ -295,31 +302,15 @@
}
@Test
- fun isWifiDefault_cellularVcnNetwork_isTrue() = runBlocking(IMMEDIATE) {
- val job = underTest.isWifiDefault.launchIn(this)
-
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
- }
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
- assertThat(underTest.isWifiDefault.value).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_cellularAndWifiTransports_usesCellular_isTrue() =
+ fun isWifiDefault_cellularVcnNetwork_isTrue() =
runBlocking(IMMEDIATE) {
val job = underTest.isWifiDefault.launchIn(this)
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
- }
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
@@ -329,117 +320,134 @@
}
@Test
- fun isWifiDefault_cellularNotVcnNetwork_isFalse() = runBlocking(IMMEDIATE) {
- val job = underTest.isWifiDefault.launchIn(this)
+ fun wifiNetwork_cellularAndWifiTransports_usesCellular_isTrue() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(mock())
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(underTest.isWifiDefault.value).isTrue()
+
+ job.cancel()
}
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
- assertThat(underTest.isWifiDefault.value).isFalse()
-
- job.cancel()
- }
-
@Test
- fun isWifiDefault_wifiNetworkLost_isFalse() = runBlocking(IMMEDIATE) {
- val job = underTest.isWifiDefault.launchIn(this)
+ fun isWifiDefault_cellularNotVcnNetwork_isFalse() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
- // First, add a network
- getDefaultNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- assertThat(underTest.isWifiDefault.value).isTrue()
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(mock())
+ }
- // WHEN the network is lost
- getDefaultNetworkCallback().onLost(NETWORK)
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
- // THEN we update to false
- assertThat(underTest.isWifiDefault.value).isFalse()
+ assertThat(underTest.isWifiDefault.value).isFalse()
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
- whenever(this.isPrimary).thenReturn(true)
- }
- val network = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(NETWORK_ID)
+ job.cancel()
}
- getNetworkCallback().onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
-
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
- assertThat(latestActive.ssid).isEqualTo(SSID)
-
- job.cancel()
- }
-
@Test
- fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun isWifiDefault_wifiNetworkLost_isFalse() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.isWifiDefault.launchIn(this)
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.isPrimary).thenReturn(true)
- whenever(this.isCarrierMerged).thenReturn(true)
+ // First, add a network
+ getDefaultNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat(underTest.isWifiDefault.value).isTrue()
+
+ // WHEN the network is lost
+ getDefaultNetworkCallback().onLost(NETWORK)
+
+ // THEN we update to false
+ assertThat(underTest.isWifiDefault.value).isFalse()
+
+ job.cancel()
}
- getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+ @Test
+ fun wifiNetwork_initiallyGetsDefault() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+ assertThat(latest).isEqualTo(WIFI_NETWORK_DEFAULT)
- job.cancel()
- }
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_primaryWifiNetworkAdded_flowHasNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+ val network = mock<Network>().apply { whenever(this.getNetId()).thenReturn(NETWORK_ID) }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(network, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+
+ job.cancel()
+ }
@Test
fun wifiNetwork_carrierMergedButInvalidSubId_flowHasInvalid() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.isPrimary).thenReturn(true)
- whenever(this.isCarrierMerged).thenReturn(true)
- whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
- }
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ whenever(this.subscriptionId).thenReturn(INVALID_SUBSCRIPTION_ID)
+ }
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK,
- createWifiNetworkCapabilities(wifiInfo),
- )
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(wifiInfo),
+ )
assertThat(latest).isInstanceOf(WifiNetworkModel.Invalid::class.java)
@@ -450,26 +458,25 @@
fun wifiNetwork_isCarrierMerged_getsCorrectValues() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
val rssi = -57
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.isPrimary).thenReturn(true)
- whenever(this.isCarrierMerged).thenReturn(true)
- whenever(this.rssi).thenReturn(rssi)
- whenever(this.subscriptionId).thenReturn(567)
- }
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ whenever(this.rssi).thenReturn(rssi)
+ whenever(this.subscriptionId).thenReturn(567)
+ }
whenever(wifiManager.calculateSignalLevel(rssi)).thenReturn(2)
whenever(wifiManager.maxSignalLevel).thenReturn(5)
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK,
- createWifiNetworkCapabilities(wifiInfo),
- )
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(wifiInfo),
+ )
assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
val latestCarrierMerged = latest as WifiNetworkModel.CarrierMerged
@@ -483,73 +490,71 @@
}
@Test
- fun wifiNetwork_notValidated_networkNotValidated() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_notValidated_networkNotValidated() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = false)
- )
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = false)
+ )
- assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse()
+ assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse()
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_validated_networkValidated() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = true)
- )
-
- assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
- whenever(this.isPrimary).thenReturn(false)
+ job.cancel()
}
- getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+ @Test
+ fun wifiNetwork_validated_networkValidated() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = true)
+ )
- job.cancel()
- }
+ assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(false)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
/** Regression test for b/266628069. */
@Test
fun wifiNetwork_transportInfoIsNotWifi_flowHasNoNetwork() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- val transportInfo = VpnTransportInfo(
- /* type= */ 0,
- /* sessionId= */ "sessionId",
- )
+ val transportInfo =
+ VpnTransportInfo(
+ /* type= */ 0,
+ /* sessionId= */ "sessionId",
+ )
getNetworkCallback()
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(transportInfo))
@@ -559,86 +564,16 @@
}
@Test
- fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
- }
-
- getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
- assertThat(latestActive.ssid).isEqualTo(SSID)
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
- whenever(this.isPrimary).thenReturn(false)
- }
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
- }
-
- getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
- assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(mock())
- }
-
- getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
-
- assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_cellularAndWifiTransports_usesCellular() =
+ fun wifiNetwork_cellularVcnNetworkAdded_flowHasNetwork() =
runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- val capabilities = mock<NetworkCapabilities>().apply {
- whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
- whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
- whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
- }
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
@@ -651,309 +586,346 @@
}
@Test
- fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_nonPrimaryCellularVcnNetworkAdded_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- // Start with the original network
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(false)
+ }
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(wifiInfo))
+ }
- // WHEN we update to a new primary network
- val newNetworkId = 456
- val newNetwork = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(newNetworkId)
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
}
- val newSsid = "CD"
- val newWifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(newSsid)
- whenever(this.isPrimary).thenReturn(true)
- }
-
- getNetworkCallback().onCapabilitiesChanged(
- newNetwork, createWifiNetworkCapabilities(newWifiInfo)
- )
-
- // THEN we use the new network
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(newNetworkId)
- assertThat(latestActive.ssid).isEqualTo(newSsid)
-
- job.cancel()
- }
@Test
- fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_cellularNotVcnNetworkAdded_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- // Start with the original network
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(mock())
+ }
- // WHEN we notify of a new but non-primary network
- val newNetworkId = 456
- val newNetwork = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(newNetworkId)
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
}
- val newSsid = "EF"
- val newWifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(newSsid)
- whenever(this.isPrimary).thenReturn(false)
- }
-
- getNetworkCallback().onCapabilitiesChanged(
- newNetwork, createWifiNetworkCapabilities(newWifiInfo)
- )
-
- // THEN we still use the original network
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
- assertThat(latestActive.ssid).isEqualTo(SSID)
-
- job.cancel()
- }
@Test
- fun wifiNetwork_newNetworkCapabilities_flowHasNewData() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_cellularAndWifiTransports_usesCellular() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- val wifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
- whenever(this.isPrimary).thenReturn(true)
+ val capabilities =
+ mock<NetworkCapabilities>().apply {
+ whenever(this.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(this.transportInfo).thenReturn(VcnTransportInfo(PRIMARY_WIFI_INFO))
+ }
+
+ getNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
+
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
}
- // Start with the original network
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(wifiInfo, isValidated = true)
- )
+ @Test
+ fun wifiNetwork_newPrimaryWifiNetwork_flowHasNewNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- // WHEN we keep the same network ID but change the SSID
- val newSsid = "CD"
- val newWifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(newSsid)
- whenever(this.isPrimary).thenReturn(true)
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+
+ // WHEN we update to a new primary network
+ val newNetworkId = 456
+ val newNetwork =
+ mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+ val newSsid = "CD"
+ val newWifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(newWifiInfo))
+
+ // THEN we use the new network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(newNetworkId)
+ assertThat(latestActive.ssid).isEqualTo(newSsid)
+
+ job.cancel()
}
- getNetworkCallback().onCapabilitiesChanged(
- NETWORK, createWifiNetworkCapabilities(newWifiInfo, isValidated = false)
- )
-
- // THEN we've updated to the new SSID
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
- assertThat(latestActive.ssid).isEqualTo(newSsid)
- assertThat(latestActive.isValidated).isFalse()
-
- job.cancel()
- }
-
@Test
- fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_newNonPrimaryWifiNetwork_flowHasOldNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- // WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
- getNetworkCallback().onLost(NETWORK)
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- // THEN there's no crash and we still have no network
- assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+ // WHEN we notify of a new but non-primary network
+ val newNetworkId = 456
+ val newNetwork =
+ mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+ val newSsid = "EF"
+ val newWifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(false)
+ }
- job.cancel()
- }
+ getNetworkCallback()
+ .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(newWifiInfo))
- @Test
- fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ // THEN we still use the original network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
-
- // WHEN we lose our current network
- getNetworkCallback().onLost(NETWORK)
-
- // THEN we update to no network
- assertThat(latest is WifiNetworkModel.Inactive).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
-
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
-
- // WHEN we lose an unknown network
- val unknownNetwork = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(543)
+ job.cancel()
}
- getNetworkCallback().onLost(unknownNetwork)
-
- // THEN we still have our previous network
- assertThat(latest is WifiNetworkModel.Active).isTrue()
- val latestActive = latest as WifiNetworkModel.Active
- assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
- assertThat(latestActive.ssid).isEqualTo(SSID)
-
- job.cancel()
- }
@Test
- fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() = runBlocking(IMMEDIATE) {
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiNetwork_newNetworkCapabilities_flowHasNewData() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
- // WHEN we update to a new network...
- val newNetworkId = 89
- val newNetwork = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(newNetworkId)
+ // Start with the original network
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(wifiInfo, isValidated = true)
+ )
+
+ // WHEN we keep the same network ID but change the SSID
+ val newSsid = "CD"
+ val newWifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(newSsid)
+ whenever(this.isPrimary).thenReturn(true)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(
+ NETWORK,
+ createWifiNetworkCapabilities(newWifiInfo, isValidated = false)
+ )
+
+ // THEN we've updated to the new SSID
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(newSsid)
+ assertThat(latestActive.isValidated).isFalse()
+
+ job.cancel()
}
- getNetworkCallback().onCapabilitiesChanged(
- newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO)
- )
- // ...and lose the old network
- getNetworkCallback().onLost(NETWORK)
- // THEN we still have the new network
- assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
+ @Test
+ fun wifiNetwork_noCurrentNetwork_networkLost_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- job.cancel()
- }
+ // WHEN we receive #onLost without any #onCapabilitiesChanged beforehand
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN there's no crash and we still have no network
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we lose our current network
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN we update to no network
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we lose an unknown network
+ val unknownNetwork = mock<Network>().apply { whenever(this.getNetId()).thenReturn(543) }
+ getNetworkCallback().onLost(unknownNetwork)
+
+ // THEN we still have our previous network
+ assertThat(latest is WifiNetworkModel.Active).isTrue()
+ val latestActive = latest as WifiNetworkModel.Active
+ assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latestActive.ssid).isEqualTo(SSID)
+
+ job.cancel()
+ }
+
+ @Test
+ fun wifiNetwork_notCurrentNetworkLost_flowHasCurrentNetwork() =
+ runBlocking(IMMEDIATE) {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we update to a new network...
+ val newNetworkId = 89
+ val newNetwork =
+ mock<Network>().apply { whenever(this.getNetId()).thenReturn(newNetworkId) }
+ getNetworkCallback()
+ .onCapabilitiesChanged(newNetwork, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ // ...and lose the old network
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN we still have the new network
+ assertThat((latest as WifiNetworkModel.Active).networkId).isEqualTo(newNetworkId)
+
+ job.cancel()
+ }
/** Regression test for b/244173280. */
@Test
- fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() = runBlocking(IMMEDIATE) {
- var latest1: WifiNetworkModel? = null
- val job1 = underTest
- .wifiNetwork
- .onEach { latest1 = it }
- .launchIn(this)
+ fun wifiNetwork_multipleSubscribers_newSubscribersGetCurrentValue() =
+ runBlocking(IMMEDIATE) {
+ var latest1: WifiNetworkModel? = null
+ val job1 = underTest.wifiNetwork.onEach { latest1 = it }.launchIn(this)
- getNetworkCallback()
- .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO))
- assertThat(latest1 is WifiNetworkModel.Active).isTrue()
- val latest1Active = latest1 as WifiNetworkModel.Active
- assertThat(latest1Active.networkId).isEqualTo(NETWORK_ID)
- assertThat(latest1Active.ssid).isEqualTo(SSID)
+ assertThat(latest1 is WifiNetworkModel.Active).isTrue()
+ val latest1Active = latest1 as WifiNetworkModel.Active
+ assertThat(latest1Active.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latest1Active.ssid).isEqualTo(SSID)
- // WHEN we add a second subscriber after having already emitted a value
- var latest2: WifiNetworkModel? = null
- val job2 = underTest
- .wifiNetwork
- .onEach { latest2 = it }
- .launchIn(this)
+ // WHEN we add a second subscriber after having already emitted a value
+ var latest2: WifiNetworkModel? = null
+ val job2 = underTest.wifiNetwork.onEach { latest2 = it }.launchIn(this)
- // THEN the second subscribe receives the already-emitted value
- assertThat(latest2 is WifiNetworkModel.Active).isTrue()
- val latest2Active = latest2 as WifiNetworkModel.Active
- assertThat(latest2Active.networkId).isEqualTo(NETWORK_ID)
- assertThat(latest2Active.ssid).isEqualTo(SSID)
+ // THEN the second subscribe receives the already-emitted value
+ assertThat(latest2 is WifiNetworkModel.Active).isTrue()
+ val latest2Active = latest2 as WifiNetworkModel.Active
+ assertThat(latest2Active.networkId).isEqualTo(NETWORK_ID)
+ assertThat(latest2Active.ssid).isEqualTo(SSID)
- job1.cancel()
- job2.cancel()
- }
+ job1.cancel()
+ job2.cancel()
+ }
@Test
- fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
- var latest: DataActivityModel? = null
- val job = underTest
- .wifiActivity
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiActivity_callbackGivesNone_activityFlowHasNone() =
+ runBlocking(IMMEDIATE) {
+ var latest: DataActivityModel? = null
+ val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
- getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
+ getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
- assertThat(latest).isEqualTo(
- DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- )
+ assertThat(latest)
+ .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
- var latest: DataActivityModel? = null
- val job = underTest
- .wifiActivity
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiActivity_callbackGivesIn_activityFlowHasIn() =
+ runBlocking(IMMEDIATE) {
+ var latest: DataActivityModel? = null
+ val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
- getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
+ getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
- assertThat(latest).isEqualTo(
- DataActivityModel(hasActivityIn = true, hasActivityOut = false)
- )
+ assertThat(latest)
+ .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = false))
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
- var latest: DataActivityModel? = null
- val job = underTest
- .wifiActivity
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiActivity_callbackGivesOut_activityFlowHasOut() =
+ runBlocking(IMMEDIATE) {
+ var latest: DataActivityModel? = null
+ val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
- getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
+ getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
- assertThat(latest).isEqualTo(
- DataActivityModel(hasActivityIn = false, hasActivityOut = true)
- )
+ assertThat(latest)
+ .isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = true))
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
- var latest: DataActivityModel? = null
- val job = underTest
- .wifiActivity
- .onEach { latest = it }
- .launchIn(this)
+ fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() =
+ runBlocking(IMMEDIATE) {
+ var latest: DataActivityModel? = null
+ val job = underTest.wifiActivity.onEach { latest = it }.launchIn(this)
- getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
+ getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
- assertThat(latest).isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
+ assertThat(latest)
+ .isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
- job.cancel()
- }
+ job.cancel()
+ }
private fun createRepo(): WifiRepositoryImpl {
return WifiRepositoryImpl(
@@ -998,14 +970,13 @@
private companion object {
const val NETWORK_ID = 45
- val NETWORK = mock<Network>().apply {
- whenever(this.getNetId()).thenReturn(NETWORK_ID)
- }
+ val NETWORK = mock<Network>().apply { whenever(this.getNetId()).thenReturn(NETWORK_ID) }
const val SSID = "AB"
- val PRIMARY_WIFI_INFO: WifiInfo = mock<WifiInfo>().apply {
- whenever(this.ssid).thenReturn(SSID)
- whenever(this.isPrimary).thenReturn(true)
- }
+ val PRIMARY_WIFI_INFO: WifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.ssid).thenReturn(SSID)
+ whenever(this.isPrimary).thenReturn(true)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 089a170..fc2277b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -22,8 +22,8 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -57,10 +57,7 @@
wifiRepository.setWifiNetwork(WifiNetworkModel.Unavailable)
var latest: String? = "default"
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
assertThat(latest).isNull()
@@ -68,238 +65,223 @@
}
@Test
- fun ssid_inactiveNetwork_outputsNull() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+ fun ssid_inactiveNetwork_outputsNull() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
- var latest: String? = "default"
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = "default"
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ assertThat(latest).isNull()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun ssid_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
- )
+ fun ssid_carrierMergedNetwork_outputsNull() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
+ )
- var latest: String? = "default"
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = "default"
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ assertThat(latest).isNull()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun ssid_isPasspointAccessPoint_outputsPasspointName() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- isPasspointAccessPoint = true,
- passpointProviderFriendlyName = "friendly",
- ))
+ fun ssid_isPasspointAccessPoint_outputsPasspointName() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 1,
+ isPasspointAccessPoint = true,
+ passpointProviderFriendlyName = "friendly",
+ )
+ )
- var latest: String? = null
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = null
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo("friendly")
+ assertThat(latest).isEqualTo("friendly")
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- isOnlineSignUpForPasspointAccessPoint = true,
- passpointProviderFriendlyName = "friendly",
- ))
+ fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 1,
+ isOnlineSignUpForPasspointAccessPoint = true,
+ passpointProviderFriendlyName = "friendly",
+ )
+ )
- var latest: String? = null
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = null
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo("friendly")
+ assertThat(latest).isEqualTo("friendly")
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun ssid_unknownSsid_outputsNull() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- ssid = WifiManager.UNKNOWN_SSID,
- ))
+ fun ssid_unknownSsid_outputsNull() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 1,
+ ssid = WifiManager.UNKNOWN_SSID,
+ )
+ )
- var latest: String? = "default"
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = "default"
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ assertThat(latest).isNull()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun ssid_validSsid_outputsSsid() = runBlocking(IMMEDIATE) {
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(
- networkId = 1,
- level = 1,
- ssid = "MyAwesomeWifiNetwork",
- ))
+ fun ssid_validSsid_outputsSsid() =
+ runBlocking(IMMEDIATE) {
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 1,
+ ssid = "MyAwesomeWifiNetwork",
+ )
+ )
- var latest: String? = null
- val job = underTest
- .ssid
- .onEach { latest = it }
- .launchIn(this)
+ var latest: String? = null
+ val job = underTest.ssid.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo("MyAwesomeWifiNetwork")
+ assertThat(latest).isEqualTo("MyAwesomeWifiNetwork")
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isEnabled_matchesRepoIsEnabled() = runBlocking(IMMEDIATE) {
- var latest: Boolean? = null
- val job = underTest
- .isEnabled
- .onEach { latest = it }
- .launchIn(this)
+ fun isEnabled_matchesRepoIsEnabled() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isEnabled.onEach { latest = it }.launchIn(this)
- wifiRepository.setIsWifiEnabled(true)
- yield()
- assertThat(latest).isTrue()
+ wifiRepository.setIsWifiEnabled(true)
+ yield()
+ assertThat(latest).isTrue()
- wifiRepository.setIsWifiEnabled(false)
- yield()
- assertThat(latest).isFalse()
+ wifiRepository.setIsWifiEnabled(false)
+ yield()
+ assertThat(latest).isFalse()
- wifiRepository.setIsWifiEnabled(true)
- yield()
- assertThat(latest).isTrue()
+ wifiRepository.setIsWifiEnabled(true)
+ yield()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isDefault_matchesRepoIsDefault() = runBlocking(IMMEDIATE) {
- var latest: Boolean? = null
- val job = underTest
- .isDefault
- .onEach { latest = it }
- .launchIn(this)
+ fun isDefault_matchesRepoIsDefault() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isDefault.onEach { latest = it }.launchIn(this)
- wifiRepository.setIsWifiDefault(true)
- yield()
- assertThat(latest).isTrue()
+ wifiRepository.setIsWifiDefault(true)
+ yield()
+ assertThat(latest).isTrue()
- wifiRepository.setIsWifiDefault(false)
- yield()
- assertThat(latest).isFalse()
+ wifiRepository.setIsWifiDefault(false)
+ yield()
+ assertThat(latest).isFalse()
- wifiRepository.setIsWifiDefault(true)
- yield()
- assertThat(latest).isTrue()
+ wifiRepository.setIsWifiDefault(true)
+ yield()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun wifiNetwork_matchesRepoWifiNetwork() = runBlocking(IMMEDIATE) {
- val wifiNetwork = WifiNetworkModel.Active(
- networkId = 45,
- isValidated = true,
- level = 3,
- ssid = "AB",
- passpointProviderFriendlyName = "friendly"
- )
- wifiRepository.setWifiNetwork(wifiNetwork)
+ fun wifiNetwork_matchesRepoWifiNetwork() =
+ runBlocking(IMMEDIATE) {
+ val wifiNetwork =
+ WifiNetworkModel.Active(
+ networkId = 45,
+ isValidated = true,
+ level = 3,
+ ssid = "AB",
+ passpointProviderFriendlyName = "friendly"
+ )
+ wifiRepository.setWifiNetwork(wifiNetwork)
- var latest: WifiNetworkModel? = null
- val job = underTest
- .wifiNetwork
- .onEach { latest = it }
- .launchIn(this)
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
- assertThat(latest).isEqualTo(wifiNetwork)
+ assertThat(latest).isEqualTo(wifiNetwork)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activity_matchesRepoWifiActivity() = runBlocking(IMMEDIATE) {
- var latest: DataActivityModel? = null
- val job = underTest
- .activity
- .onEach { latest = it }
- .launchIn(this)
+ fun activity_matchesRepoWifiActivity() =
+ runBlocking(IMMEDIATE) {
+ var latest: DataActivityModel? = null
+ val job = underTest.activity.onEach { latest = it }.launchIn(this)
- val activity1 = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity1)
- yield()
- assertThat(latest).isEqualTo(activity1)
+ val activity1 = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity1)
+ yield()
+ assertThat(latest).isEqualTo(activity1)
- val activity2 = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity2)
- yield()
- assertThat(latest).isEqualTo(activity2)
+ val activity2 = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity2)
+ yield()
+ assertThat(latest).isEqualTo(activity2)
- val activity3 = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity3)
- yield()
- assertThat(latest).isEqualTo(activity3)
+ val activity3 = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity3)
+ yield()
+ assertThat(latest).isEqualTo(activity3)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isForceHidden_repoHasWifiHidden_outputsTrue() = runBlocking(IMMEDIATE) {
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+ fun isForceHidden_repoHasWifiHidden_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
- var latest: Boolean? = null
- val job = underTest
- .isForceHidden
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() = runBlocking(IMMEDIATE) {
- connectivityRepository.setForceHiddenIcons(setOf())
+ fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ connectivityRepository.setForceHiddenIcons(setOf())
- var latest: Boolean? = null
- val job = underTest
- .isForceHidden
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
}
private val IMMEDIATE = Dispatchers.Main.immediate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index 824cebd..ab4e93c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.wifi.data.model
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableRowLogger
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Companion.MIN_VALID_LEVEL
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index b8ace2f..60f564e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -38,11 +38,11 @@
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import com.android.systemui.util.mockito.whenever
@@ -62,16 +62,11 @@
private lateinit var testableLooper: TestableLooper
- @Mock
- private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
- @Mock
- private lateinit var logger: ConnectivityPipelineLogger
- @Mock
- private lateinit var tableLogBuffer: TableLogBuffer
- @Mock
- private lateinit var connectivityConstants: ConnectivityConstants
- @Mock
- private lateinit var wifiConstants: WifiConstants
+ @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
+ @Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var connectivityConstants: ConnectivityConstants
+ @Mock private lateinit var wifiConstants: WifiConstants
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var connectivityRepository: FakeConnectivityRepository
private lateinit var wifiRepository: FakeWifiRepository
@@ -91,25 +86,28 @@
wifiRepository.setIsWifiEnabled(true)
interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
- airplaneModeViewModel = AirplaneModeViewModelImpl(
- AirplaneModeInteractor(
- airplaneModeRepository,
- connectivityRepository,
- ),
- logger,
- scope,
- )
- viewModel = WifiViewModel(
- airplaneModeViewModel,
- connectivityConstants,
- context,
- logger,
- tableLogBuffer,
- interactor,
- scope,
- statusBarPipelineFlags,
- wifiConstants,
- ).home
+ airplaneModeViewModel =
+ AirplaneModeViewModelImpl(
+ AirplaneModeInteractor(
+ airplaneModeRepository,
+ connectivityRepository,
+ ),
+ logger,
+ scope,
+ )
+ viewModel =
+ WifiViewModel(
+ airplaneModeViewModel,
+ connectivityConstants,
+ context,
+ logger,
+ tableLogBuffer,
+ interactor,
+ scope,
+ statusBarPipelineFlags,
+ wifiConstants,
+ )
+ .home
}
// Note: The following tests are more like integration tests, since they stand up a full
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index b932837..648d7a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -36,11 +36,11 @@
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index e5cfec9..45ebb39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -29,11 +29,11 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -79,14 +79,15 @@
wifiRepository.setIsWifiEnabled(true)
interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
- airplaneModeViewModel = AirplaneModeViewModelImpl(
- AirplaneModeInteractor(
- airplaneModeRepository,
- connectivityRepository,
- ),
- logger,
- scope,
- )
+ airplaneModeViewModel =
+ AirplaneModeViewModelImpl(
+ AirplaneModeInteractor(
+ airplaneModeRepository,
+ connectivityRepository,
+ ),
+ logger,
+ scope,
+ )
createAndSetViewModel()
}
@@ -104,451 +105,386 @@
// instances. There are also some tests that verify all 3 instances received the same data.
@Test
- fun wifiIcon_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
- var latestHome: WifiIcon? = null
- val jobHome = underTest
- .home
- .wifiIcon
- .onEach { latestHome = it }
- .launchIn(this)
+ fun wifiIcon_allLocationViewModelsReceiveSameData() =
+ runBlocking(IMMEDIATE) {
+ var latestHome: WifiIcon? = null
+ val jobHome = underTest.home.wifiIcon.onEach { latestHome = it }.launchIn(this)
- var latestKeyguard: WifiIcon? = null
- val jobKeyguard = underTest
- .keyguard
- .wifiIcon
- .onEach { latestKeyguard = it }
- .launchIn(this)
+ var latestKeyguard: WifiIcon? = null
+ val jobKeyguard =
+ underTest.keyguard.wifiIcon.onEach { latestKeyguard = it }.launchIn(this)
- var latestQs: WifiIcon? = null
- val jobQs = underTest
- .qs
- .wifiIcon
- .onEach { latestQs = it }
- .launchIn(this)
+ var latestQs: WifiIcon? = null
+ val jobQs = underTest.qs.wifiIcon.onEach { latestQs = it }.launchIn(this)
- wifiRepository.setWifiNetwork(
- WifiNetworkModel.Active(
- NETWORK_ID,
- isValidated = true,
- level = 1
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1)
)
- )
- yield()
+ yield()
- assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
- assertThat(latestHome).isEqualTo(latestKeyguard)
- assertThat(latestKeyguard).isEqualTo(latestQs)
+ assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
+ assertThat(latestHome).isEqualTo(latestKeyguard)
+ assertThat(latestKeyguard).isEqualTo(latestQs)
- jobHome.cancel()
- jobKeyguard.cancel()
- jobQs.cancel()
- }
+ jobHome.cancel()
+ jobKeyguard.cancel()
+ jobQs.cancel()
+ }
@Test
- fun activity_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activity_showActivityConfigFalse_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var activityIn: Boolean? = null
- val activityInJob = underTest
- .home
- .isActivityInViewVisible
- .onEach { activityIn = it }
- .launchIn(this)
+ var activityIn: Boolean? = null
+ val activityInJob =
+ underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
- var activityOut: Boolean? = null
- val activityOutJob = underTest
- .home
- .isActivityOutViewVisible
- .onEach { activityOut = it }
- .launchIn(this)
+ var activityOut: Boolean? = null
+ val activityOutJob =
+ underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
- var activityContainer: Boolean? = null
- val activityContainerJob = underTest
- .home
- .isActivityContainerVisible
- .onEach { activityContainer = it }
- .launchIn(this)
+ var activityContainer: Boolean? = null
+ val activityContainerJob =
+ underTest.home.isActivityContainerVisible
+ .onEach { activityContainer = it }
+ .launchIn(this)
- // Verify that on launch, we receive false.
- assertThat(activityIn).isFalse()
- assertThat(activityOut).isFalse()
- assertThat(activityContainer).isFalse()
+ // Verify that on launch, we receive false.
+ assertThat(activityIn).isFalse()
+ assertThat(activityOut).isFalse()
+ assertThat(activityContainer).isFalse()
- activityInJob.cancel()
- activityOutJob.cancel()
- activityContainerJob.cancel()
- }
+ activityInJob.cancel()
+ activityOutJob.cancel()
+ activityContainerJob.cancel()
+ }
@Test
- fun activity_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activity_showActivityConfigFalse_noUpdatesReceived() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var activityIn: Boolean? = null
- val activityInJob = underTest
- .home
- .isActivityInViewVisible
- .onEach { activityIn = it }
- .launchIn(this)
+ var activityIn: Boolean? = null
+ val activityInJob =
+ underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
- var activityOut: Boolean? = null
- val activityOutJob = underTest
- .home
- .isActivityOutViewVisible
- .onEach { activityOut = it }
- .launchIn(this)
+ var activityOut: Boolean? = null
+ val activityOutJob =
+ underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
- var activityContainer: Boolean? = null
- val activityContainerJob = underTest
- .home
- .isActivityContainerVisible
- .onEach { activityContainer = it }
- .launchIn(this)
+ var activityContainer: Boolean? = null
+ val activityContainerJob =
+ underTest.home.isActivityContainerVisible
+ .onEach { activityContainer = it }
+ .launchIn(this)
- // WHEN we update the repo to have activity
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ // WHEN we update the repo to have activity
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- // THEN we didn't update to the new activity (because our config is false)
- assertThat(activityIn).isFalse()
- assertThat(activityOut).isFalse()
- assertThat(activityContainer).isFalse()
+ // THEN we didn't update to the new activity (because our config is false)
+ assertThat(activityIn).isFalse()
+ assertThat(activityOut).isFalse()
+ assertThat(activityContainer).isFalse()
- activityInJob.cancel()
- activityOutJob.cancel()
- activityContainerJob.cancel()
- }
+ activityInJob.cancel()
+ activityOutJob.cancel()
+ activityContainerJob.cancel()
+ }
@Test
- fun activity_nullSsid_outputsFalse() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
+ fun activity_nullSsid_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
- wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1))
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
+ )
- var activityIn: Boolean? = null
- val activityInJob = underTest
- .home
- .isActivityInViewVisible
- .onEach { activityIn = it }
- .launchIn(this)
+ var activityIn: Boolean? = null
+ val activityInJob =
+ underTest.home.isActivityInViewVisible.onEach { activityIn = it }.launchIn(this)
- var activityOut: Boolean? = null
- val activityOutJob = underTest
- .home
- .isActivityOutViewVisible
- .onEach { activityOut = it }
- .launchIn(this)
+ var activityOut: Boolean? = null
+ val activityOutJob =
+ underTest.home.isActivityOutViewVisible.onEach { activityOut = it }.launchIn(this)
- var activityContainer: Boolean? = null
- val activityContainerJob = underTest
- .home
- .isActivityContainerVisible
- .onEach { activityContainer = it }
- .launchIn(this)
+ var activityContainer: Boolean? = null
+ val activityContainerJob =
+ underTest.home.isActivityContainerVisible
+ .onEach { activityContainer = it }
+ .launchIn(this)
- // WHEN we update the repo to have activity
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ // WHEN we update the repo to have activity
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- // THEN we still output false because our network's SSID is null
- assertThat(activityIn).isFalse()
- assertThat(activityOut).isFalse()
- assertThat(activityContainer).isFalse()
+ // THEN we still output false because our network's SSID is null
+ assertThat(activityIn).isFalse()
+ assertThat(activityOut).isFalse()
+ assertThat(activityContainer).isFalse()
- activityInJob.cancel()
- activityOutJob.cancel()
- activityContainerJob.cancel()
- }
+ activityInJob.cancel()
+ activityOutJob.cancel()
+ activityContainerJob.cancel()
+ }
@Test
- fun activity_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activity_allLocationViewModelsReceiveSameData() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latestHome: Boolean? = null
- val jobHome = underTest
- .home
- .isActivityInViewVisible
- .onEach { latestHome = it }
- .launchIn(this)
+ var latestHome: Boolean? = null
+ val jobHome =
+ underTest.home.isActivityInViewVisible.onEach { latestHome = it }.launchIn(this)
- var latestKeyguard: Boolean? = null
- val jobKeyguard = underTest
- .keyguard
- .isActivityInViewVisible
- .onEach { latestKeyguard = it }
- .launchIn(this)
+ var latestKeyguard: Boolean? = null
+ val jobKeyguard =
+ underTest.keyguard.isActivityInViewVisible
+ .onEach { latestKeyguard = it }
+ .launchIn(this)
- var latestQs: Boolean? = null
- val jobQs = underTest
- .qs
- .isActivityInViewVisible
- .onEach { latestQs = it }
- .launchIn(this)
+ var latestQs: Boolean? = null
+ val jobQs = underTest.qs.isActivityInViewVisible.onEach { latestQs = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latestHome).isTrue()
- assertThat(latestKeyguard).isTrue()
- assertThat(latestQs).isTrue()
+ assertThat(latestHome).isTrue()
+ assertThat(latestKeyguard).isTrue()
+ assertThat(latestQs).isTrue()
- jobHome.cancel()
- jobKeyguard.cancel()
- jobQs.cancel()
- }
+ jobHome.cancel()
+ jobKeyguard.cancel()
+ jobQs.cancel()
+ }
@Test
- fun activityIn_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityIn_hasActivityInTrue_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityInViewVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.home.isActivityInViewVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityIn_hasActivityInFalse_outputsFalse() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityIn_hasActivityInFalse_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityInViewVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.home.isActivityInViewVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityOut_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityOut_hasActivityOutTrue_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityOutViewVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.home.isActivityOutViewVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityOut_hasActivityOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityOut_hasActivityOutFalse_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityOutViewVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job = underTest.home.isActivityOutViewVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityContainer_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityContainer_hasActivityInTrue_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityContainerVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job =
+ underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityContainer_hasActivityOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityContainer_hasActivityOutTrue_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityContainerVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job =
+ underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityContainer_inAndOutTrue_outputsTrue() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityContainer_inAndOutTrue_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityContainerVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job =
+ underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun activityContainer_inAndOutFalse_outputsFalse() = runBlocking(IMMEDIATE) {
- whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
- wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
+ fun activityContainer_inAndOutFalse_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
+ createAndSetViewModel()
+ wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
- var latest: Boolean? = null
- val job = underTest
- .home
- .isActivityContainerVisible
- .onEach { latest = it }
- .launchIn(this)
+ var latest: Boolean? = null
+ val job =
+ underTest.home.isActivityContainerVisible.onEach { latest = it }.launchIn(this)
- val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- wifiRepository.setWifiActivity(activity)
- yield()
+ val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ wifiRepository.setWifiActivity(activity)
+ yield()
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun airplaneSpacer_notAirplaneMode_outputsFalse() = runBlocking(IMMEDIATE) {
- var latest: Boolean? = null
- val job = underTest
- .qs
- .isAirplaneSpacerVisible
- .onEach { latest = it }
- .launchIn(this)
+ fun airplaneSpacer_notAirplaneMode_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
- airplaneModeRepository.setIsAirplaneMode(false)
- yield()
+ airplaneModeRepository.setIsAirplaneMode(false)
+ yield()
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun airplaneSpacer_airplaneForceHidden_outputsFalse() = runBlocking(IMMEDIATE) {
- var latest: Boolean? = null
- val job = underTest
- .qs
- .isAirplaneSpacerVisible
- .onEach { latest = it }
- .launchIn(this)
+ fun airplaneSpacer_airplaneForceHidden_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
- airplaneModeRepository.setIsAirplaneMode(true)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.AIRPLANE))
- yield()
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.AIRPLANE))
+ yield()
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun airplaneSpacer_airplaneIconVisible_outputsTrue() = runBlocking(IMMEDIATE) {
- var latest: Boolean? = null
- val job = underTest
- .qs
- .isAirplaneSpacerVisible
- .onEach { latest = it }
- .launchIn(this)
+ fun airplaneSpacer_airplaneIconVisible_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.qs.isAirplaneSpacerVisible.onEach { latest = it }.launchIn(this)
- airplaneModeRepository.setIsAirplaneMode(true)
- yield()
+ airplaneModeRepository.setIsAirplaneMode(true)
+ yield()
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- job.cancel()
- }
+ job.cancel()
+ }
private fun createAndSetViewModel() {
// [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
// creations rely on certain config values that we mock out in individual tests. This method
// allows tests to create the view model only after those configs are correctly set up.
- underTest = WifiViewModel(
- airplaneModeViewModel,
- connectivityConstants,
- context,
- logger,
- tableLogBuffer,
- interactor,
- scope,
- statusBarPipelineFlags,
- wifiConstants,
- )
+ underTest =
+ WifiViewModel(
+ airplaneModeViewModel,
+ connectivityConstants,
+ context,
+ logger,
+ tableLogBuffer,
+ interactor,
+ scope,
+ statusBarPipelineFlags,
+ wifiConstants,
+ )
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 8660d09..0257ebd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -28,7 +28,6 @@
import android.os.UserManager
import android.provider.Settings
import androidx.test.filters.SmallTest
-import com.android.internal.R.drawable.ic_account_circle
import com.android.internal.logging.UiEventLogger
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
@@ -87,6 +86,7 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var manager: UserManager
+ @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@Mock private lateinit var activityManager: ActivityManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@@ -145,6 +145,7 @@
featureFlags = featureFlags,
),
manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
@@ -848,6 +849,50 @@
assertThat(selectedUser()).isNotNull()
}
+ @Test
+ fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(true)
+ whenever(manager.getUserSwitchability(any()))
+ .thenReturn(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED)
+ val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+ userRepository.setSettings(
+ UserSwitcherSettingsModel(
+ isUserSwitcherEnabled = true,
+ isAddUsersFromLockscreen = true
+ )
+ )
+
+ runCurrent()
+ underTest.userRecords.value
+ .filter { it.info == null }
+ .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+ }
+
+ @Test
+ fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() =
+ testScope.runTest {
+ keyguardRepository.setKeyguardShowing(true)
+ whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true)
+ whenever(manager.isUserUnlocked(anyInt())).thenReturn(false)
+ val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+ userRepository.setSettings(
+ UserSwitcherSettingsModel(
+ isUserSwitcherEnabled = true,
+ isAddUsersFromLockscreen = true
+ )
+ )
+
+ runCurrent()
+ underTest.userRecords.value
+ .filter { it.info == null }
+ .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+ }
+
private fun assertUsers(
models: List<UserModel>?,
count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 8a35cb0..2fedb87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -41,6 +41,7 @@
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.util.mockito.mock
@@ -71,6 +72,7 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var activityManager: ActivityManager
@Mock private lateinit var manager: UserManager
+ @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@@ -252,6 +254,7 @@
),
featureFlags = featureFlags,
manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 1337d1b..166b909 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -41,6 +41,7 @@
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -72,6 +73,7 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var activityManager: ActivityManager
@Mock private lateinit var manager: UserManager
+ @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@@ -154,6 +156,7 @@
),
featureFlags = featureFlags,
manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index 5ef62c1..b367a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -60,6 +60,11 @@
mCallback = callback;
}
+ public FakeConditionalCoreStartable(Monitor monitor, Callback callback) {
+ super(monitor);
+ mCallback = callback;
+ }
+
@Override
protected void onStart() {
mCallback.onStart();
@@ -122,6 +127,31 @@
verify(mMonitor).removeSubscription(mSubscriptionToken);
}
+ @Test
+ public void testOnStartCallbackWithNoConditions() {
+ final CoreStartable coreStartable =
+ new FakeConditionalCoreStartable(mMonitor,
+ mCallback);
+
+ when(mMonitor.addSubscription(any())).thenReturn(mSubscriptionToken);
+ coreStartable.start();
+
+ final ArgumentCaptor<Monitor.Subscription> subscriptionCaptor = ArgumentCaptor.forClass(
+ Monitor.Subscription.class);
+ verify(mMonitor).addSubscription(subscriptionCaptor.capture());
+
+ final Monitor.Subscription subscription = subscriptionCaptor.getValue();
+
+ assertThat(subscription.getConditions()).isEmpty();
+
+ verify(mCallback, never()).onStart();
+
+ subscription.getCallback().onConditionsChanged(true);
+
+ verify(mCallback).onStart();
+ verify(mMonitor).removeSubscription(mSubscriptionToken);
+ }
+
/**
* Verifies that {@link ConditionalCoreStartable#bootCompleted()} ()} is predicated on
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
new file mode 100644
index 0000000..7ee05d0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/condition/SelfExecutingMonitor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.systemui.condition;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+/**
+ * {@link SelfExecutingMonitor} creates a monitor that independently executes its logic through
+ * a {@link FakeExecutor}, which is ran at when a subscription is added and removed.
+ */
+public class SelfExecutingMonitor extends Monitor {
+ private final FakeExecutor mExecutor;
+
+ /**
+ * Default constructor that allows specifying the FakeExecutor to use.
+ */
+ public SelfExecutingMonitor(FakeExecutor executor) {
+ super(executor);
+ mExecutor = executor;
+ }
+
+ @Override
+ public Subscription.Token addSubscription(@NonNull Subscription subscription) {
+ final Subscription.Token result = super.addSubscription(subscription);
+ mExecutor.runAllReady();
+ return result;
+ }
+
+ @Override
+ public void removeSubscription(@NonNull Subscription.Token token) {
+ super.removeSubscription(token);
+ mExecutor.runNextReady();
+ }
+
+ /**
+ * Creates a {@link SelfExecutingMonitor} with a self-managed {@link FakeExecutor}. Use only
+ * for cases where condition state only will be set at when a subscription is added.
+ */
+ public static SelfExecutingMonitor createInstance() {
+ final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ final FakeExecutor mExecutor = new FakeExecutor(mFakeSystemClock);
+ return new SelfExecutingMonitor(mExecutor);
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0589cfc..cf880eb 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1512,6 +1512,7 @@
case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
if (msg.arg1 != BluetoothProfile.HEADSET) {
synchronized (mDeviceStateLock) {
+ mBtHelper.onBtProfileDisconnected(msg.arg1);
mDeviceInventory.onBtProfileDisconnected(msg.arg1);
}
} else {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 6cd42f8..f959821 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -279,7 +279,11 @@
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
- mA2dp.setAvrcpAbsoluteVolume(index);
+ try {
+ mA2dp.setAvrcpAbsoluteVolume(index);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while changing abs volume", e);
+ }
}
/*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -287,7 +291,12 @@
if (mA2dp == null) {
return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
- final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+ final BluetoothCodecStatus btCodecStatus = null;
+ try {
+ mA2dp.getCodecStatus(device);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while getting status of " + device, e);
+ }
if (btCodecStatus == null) {
return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
@@ -421,7 +430,11 @@
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
- mLeAudio.setVolume(volume);
+ try {
+ mLeAudio.setVolume(volume);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while setting LE volume", e);
+ }
}
/*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -447,7 +460,11 @@
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
}
- mHearingAid.setVolume(gainDB);
+ try {
+ mHearingAid.setVolume(gainDB);
+ } catch (Exception e) {
+ Log.i(TAG, "Exception while setting hearing aid volume", e);
+ }
}
/*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -487,6 +504,35 @@
mBluetoothHeadset = null;
}
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = null;
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ mLeAudio = null;
+ break;
+
+ case BluetoothProfile.A2DP_SINK:
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // shouldn't be received here as profile doesn't involve BtHelper
+ Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+ + BluetoothProfile.getProfileName(profile));
+ break;
+
+ default:
+ // Not a valid profile to disconnect
+ Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+ + BluetoothProfile.getProfileName(profile));
+ break;
+ }
+ }
+
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -672,7 +718,6 @@
public void onServiceConnected(int profile, BluetoothProfile proxy) {
switch(profile) {
case BluetoothProfile.A2DP:
- case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
@@ -682,6 +727,10 @@
mDeviceBroker.postBtProfileConnected(profile, proxy);
break;
+ case BluetoothProfile.A2DP_SINK:
+ // no A2DP sink functionality handled by BtHelper
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // no broadcast functionality handled by BtHelper
default:
break;
}
@@ -690,14 +739,19 @@
switch (profile) {
case BluetoothProfile.A2DP:
- case BluetoothProfile.A2DP_SINK:
case BluetoothProfile.HEADSET:
case BluetoothProfile.HEARING_AID:
case BluetoothProfile.LE_AUDIO:
- case BluetoothProfile.LE_AUDIO_BROADCAST:
+ AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "BT profile service: disconnecting "
+ + BluetoothProfile.getProfileName(profile) + " profile"));
mDeviceBroker.postBtProfileDisconnected(profile);
break;
+ case BluetoothProfile.A2DP_SINK:
+ // no A2DP sink functionality handled by BtHelper
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ // no broadcast functionality handled by BtHelper
default:
break;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 787bfb0..7d390415 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -228,7 +228,16 @@
@Override
public void onError(int errorCode, int vendorCode) {
- super.onError(errorCode, vendorCode);
+ if (getContext().getResources().getBoolean(R.bool.config_powerPressMapping)
+ && errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR
+ && vendorCode == getContext().getResources()
+ .getInteger(R.integer.config_powerPressCode)) {
+ // Translating vendor code to internal code
+ super.onError(BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED,
+ 0 /* vendorCode */);
+ } else {
+ super.onError(errorCode, vendorCode);
+ }
if (errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBRATION) {
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 612d906..14b19fb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -37,6 +37,7 @@
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.R;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
@@ -143,7 +144,17 @@
}
});
mCallback.onBiometricAction(BiometricStateListener.ACTION_SENSOR_TOUCH);
- super.onAcquired(acquiredInfo, vendorCode);
+
+ if (getContext().getResources().getBoolean(R.bool.config_powerPressMapping)
+ && acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR
+ && vendorCode == getContext().getResources()
+ .getInteger(R.integer.config_powerPressCode)) {
+ // Translating vendor code to internal code
+ super.onAcquired(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED,
+ 0 /* vendorCode */);
+ } else {
+ super.onAcquired(acquiredInfo, vendorCode);
+ }
}
@Override
@@ -270,8 +281,5 @@
}
@Override
- public void onPowerPressed() {
- onAcquired(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED,
- 0 /* vendorCode */);
- }
+ public void onPowerPressed() {}
}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index dc52990..1fb00ef 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -16,6 +16,8 @@
package com.android.server.location.injector;
+import static com.android.server.location.LocationManagerService.TAG;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -23,6 +25,7 @@
import android.os.SystemClock;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.util.Log;
import com.android.server.FgThread;
@@ -67,8 +70,12 @@
}
synchronized (SystemEmergencyHelper.this) {
- mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
- intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ try {
+ mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
+ intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Failed to call TelephonyManager.isEmergencyNumber().", e);
+ }
}
}
}, new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL));
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 651bb93..5d1a581 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3262,8 +3262,7 @@
}
final PowerGroup powerGroup = mPowerGroups.get(groupId);
wakefulness = powerGroup.getWakefulnessLocked();
- if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
- powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
+ if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup);
powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
} else {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 00e3188..db8079a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -45,6 +45,7 @@
import static android.view.WindowManager.TransitionFlags;
import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_FILLS_TASK;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
@@ -1736,7 +1737,7 @@
? activityRecord.getOrganizedTaskFragment()
: taskFragment.getOrganizedTaskFragment();
if (organizedTf != null && organizedTf.getAnimationParams()
- .getAnimationBackgroundColor() != 0) {
+ .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
// This window is embedded and has an animation background color set on the
// TaskFragment. Pass this color with this window, so the handler can use it as
// the animation background color if needed,
@@ -1748,10 +1749,11 @@
final Task parentTask = activityRecord != null
? activityRecord.getTask()
: taskFragment.getTask();
- backgroundColor = ColorUtils.setAlphaComponent(
- parentTask.getTaskDescription().getBackgroundColor(), 255);
+ backgroundColor = parentTask.getTaskDescription().getBackgroundColor();
}
- change.setBackgroundColor(backgroundColor);
+ // Set to opaque for animation background to prevent it from exposing the blank
+ // background or content below.
+ change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
}
change.setRotation(info.mRotation, endRotation);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 9a20354..ce03244 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -33,6 +33,7 @@
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -3168,7 +3169,7 @@
? activityRecord.getOrganizedTaskFragment()
: taskFragment.getOrganizedTaskFragment();
if (organizedTf != null && organizedTf.getAnimationParams()
- .getAnimationBackgroundColor() != 0) {
+ .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
// This window is embedded and has an animation background color set on the
// TaskFragment. Pass this color with this window, so the handler can use it
// as the animation background color if needed,
@@ -3181,11 +3182,14 @@
final Task parentTask = activityRecord != null
? activityRecord.getTask()
: taskFragment.getTask();
- backgroundColorForTransition = ColorUtils.setAlphaComponent(
- parentTask.getTaskDescription().getBackgroundColor(), 255);
+ backgroundColorForTransition = parentTask.getTaskDescription()
+ .getBackgroundColor();
}
}
- animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
+ // Set to opaque for animation background to prevent it from exposing the blank
+ // background or content below.
+ animationRunnerBuilder.setTaskBackgroundColor(ColorUtils.setAlphaComponent(
+ backgroundColorForTransition, 255));
}
animationRunnerBuilder.build()
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 3c735e3..4915c64 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -16,6 +16,8 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -35,6 +37,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
@@ -54,6 +57,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -335,6 +339,21 @@
showHideOverlay(c -> c.onLockoutPermanent());
}
+ @Test
+ public void testPowerPressForwardsErrorMessage() throws RemoteException {
+ final FingerprintAuthenticationClient client = createClient();
+ final int testVendorPowerPressCode = 1;
+ when(mContext.getOrCreateTestableResources().getResources()
+ .getBoolean(R.bool.config_powerPressMapping)).thenReturn(true);
+ when(mContext.getOrCreateTestableResources().getResources()
+ .getInteger(R.integer.config_powerPressCode)).thenReturn(testVendorPowerPressCode);
+
+ client.onError(FINGERPRINT_ERROR_VENDOR, testVendorPowerPressCode);
+
+ verify(mClientMonitorCallbackConverter).onError(anyInt(), anyInt(),
+ eq(BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED), anyInt());
+ }
+
private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block)
throws RemoteException {
final FingerprintAuthenticationClient client = createClient();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 837b553..7e29a76 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -16,7 +16,7 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
import static com.google.common.truth.Truth.assertThat;
@@ -28,10 +28,10 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.PointerContext;
@@ -48,6 +48,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
@@ -66,7 +67,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
import java.util.function.Consumer;
@Presubmit
@@ -258,11 +258,16 @@
@Test
public void testPowerPressForwardsAcquireMessage() throws RemoteException {
final FingerprintEnrollClient client = createClient();
- client.start(mCallback);
- client.onPowerPressed();
+ final int testVendorPowerPressCode = 1;
+ when(mContext.getOrCreateTestableResources().getResources()
+ .getBoolean(R.bool.config_powerPressMapping)).thenReturn(true);
+ when(mContext.getOrCreateTestableResources().getResources()
+ .getInteger(R.integer.config_powerPressCode)).thenReturn(testVendorPowerPressCode);
+
+ client.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, testVendorPowerPressCode);
verify(mClientMonitorCallbackConverter).onAcquired(anyInt(),
- eq(FINGERPRINT_ACQUIRED_POWER_PRESSED), anyInt());
+ eq(BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_POWER_PRESSED), anyInt());
}
private void showHideOverlay(Consumer<FingerprintEnrollClient> block)