Merge "Trigger unfold animation without treshold while on keyguard" into tm-qpr-dev
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4be5e14..06399b9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1129,6 +1129,17 @@
public static final long OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN = 218959984L;
/**
+ * Enables sending fake focus for unfocused apps in splitscreen. Some game engines
+ * wait to get focus before drawing the content of the app so fake focus helps them to avoid
+ * staying blacked out when they are resumed and do not have focus yet.
+ * @hide
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;
+
+ /**
* Compares activity window layout min width/height with require space for multi window to
* determine if it can be put into multi window mode.
*/
diff --git a/core/java/android/service/chooser/OWNERS b/core/java/android/service/chooser/OWNERS
index a5acba5..dcd4a7b 100644
--- a/core/java/android/service/chooser/OWNERS
+++ b/core/java/android/service/chooser/OWNERS
@@ -1,4 +1,3 @@
-asc@google.com
-mpietal@google.com
-dsandler@android.com
-dsandler@google.com
+mrcasey@google.com
+ayepin@google.com
+joshtrask@google.com
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 3833976..970728f 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -2,6 +2,7 @@
per-file *Resolver* = file:/packages/SystemUI/OWNERS
per-file *Chooser* = file:/packages/SystemUI/OWNERS
per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
+per-file AbstractMultiProfilePagerAdapter.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
@@ -11,4 +12,4 @@
per-file *Voice* = file:/core/java/android/service/voice/OWNERS
# System language settings
-per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
\ No newline at end of file
+per-file *Locale* = file:platform/packages/apps/Settings:/src/com/android/settings/localepicker/OWNERS
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
index c6537c0..b6ea9dd 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityWorkProfileTest.java
@@ -16,11 +16,14 @@
package com.android.internal.app;
+import static android.util.PollingCheck.waitFor;
+
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -49,6 +52,8 @@
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.ChooserActivityWorkProfileTest.TestCase.Tab;
+import junit.framework.AssertionFailedError;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -331,8 +336,17 @@
final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
: R.string.resolver_personal_tab;
- onView(withText(stringId)).perform(click());
- waitForIdle();
+ waitFor(() -> {
+ onView(withText(stringId)).perform(click());
+ waitForIdle();
+
+ try {
+ onView(withText(stringId)).check(matches(isSelected()));
+ return true;
+ } catch (AssertionFailedError e) {
+ return false;
+ }
+ });
onView(withId(R.id.contentPanel))
.perform(swipeUp());
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
index ce68906..4a79f5e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityWorkProfileTest.java
@@ -16,11 +16,14 @@
package com.android.internal.app;
+import static android.util.PollingCheck.waitFor;
+
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isSelected;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -48,6 +51,8 @@
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.ResolverActivityWorkProfileTest.TestCase.Tab;
+import junit.framework.AssertionFailedError;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -309,8 +314,17 @@
final int stringId = tab == Tab.WORK ? R.string.resolver_work_tab
: R.string.resolver_personal_tab;
- onView(withText(stringId)).perform(click());
- waitForIdle();
+ waitFor(() -> {
+ onView(withText(stringId)).perform(click());
+ waitForIdle();
+
+ try {
+ onView(withText(stringId)).check(matches(isSelected()));
+ return true;
+ } catch (AssertionFailedError e) {
+ return false;
+ }
+ });
onView(withId(R.id.contentPanel))
.perform(swipeUp());
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index dc4b563..a5b192c 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -63,6 +63,12 @@
sdk_version: "current",
}
+android_library_import {
+ name: "window-extensions-core",
+ aars: ["window-extensions-core-release.aar"],
+ sdk_version: "current",
+}
+
java_library {
name: "androidx.window.extensions",
srcs: [
@@ -70,7 +76,10 @@
"src/androidx/window/util/**/*.java",
"src/androidx/window/common/**/*.java",
],
- static_libs: ["window-extensions"],
+ static_libs: [
+ "window-extensions",
+ "window-extensions-core",
+ ],
installable: true,
sdk_version: "core_platform",
system_ext_specific: true,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 1af1313..85812ea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -75,7 +75,10 @@
import androidx.annotation.Nullable;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.extensions.WindowExtensionsImpl;
import androidx.window.extensions.WindowExtensionsProvider;
+import androidx.window.extensions.core.util.function.Consumer;
+import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -86,7 +89,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* Main controller class that manages split states and presentation.
@@ -112,7 +114,7 @@
/**
* A developer-defined {@link SplitAttributes} calculator to compute the current
* {@link SplitAttributes} with the current device and window states.
- * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)}
+ * It is registered via {@link #setSplitAttributesCalculator(Function)}
* and unregistered via {@link #clearSplitAttributesCalculator()}.
* This is called when:
* <ul>
@@ -125,7 +127,7 @@
*/
@GuardedBy("mLock")
@Nullable
- private SplitAttributesCalculator mSplitAttributesCalculator;
+ private Function<SplitAttributesCalculatorParams, SplitAttributes> mSplitAttributesCalculator;
/**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
@@ -138,6 +140,7 @@
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
/** Callback to Jetpack to notify about changes to split states. */
+ @GuardedBy("mLock")
@Nullable
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
@@ -171,7 +174,8 @@
mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
}
- private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+ private class FoldingFeatureListener
+ implements java.util.function.Consumer<List<CommonFoldingFeature>> {
@Override
public void accept(List<CommonFoldingFeature> foldingFeatures) {
synchronized (mLock) {
@@ -212,7 +216,8 @@
}
@Override
- public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+ public void setSplitAttributesCalculator(
+ @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) {
synchronized (mLock) {
mSplitAttributesCalculator = calculator;
}
@@ -227,7 +232,7 @@
@GuardedBy("mLock")
@Nullable
- SplitAttributesCalculator getSplitAttributesCalculator() {
+ Function<SplitAttributesCalculatorParams, SplitAttributes> getSplitAttributesCalculator() {
return mSplitAttributesCalculator;
}
@@ -240,9 +245,22 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
+ * @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
+ * {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
*/
+ @Deprecated
@Override
- public void setSplitInfoCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+ public void setSplitInfoCallback(
+ @NonNull java.util.function.Consumer<List<SplitInfo>> callback) {
+ Consumer<List<SplitInfo>> oemConsumer = callback::accept;
+ setSplitInfoCallback(oemConsumer);
+ }
+
+ /**
+ * Registers the split organizer callback to notify about changes to active splits.
+ * @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
+ */
+ public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
synchronized (mLock) {
mEmbeddingCallback = callback;
updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 9db9f87..41580a0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -42,11 +42,11 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.window.extensions.core.util.function.Function;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
-import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams;
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -522,7 +522,8 @@
@NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
- final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
+ final Function<SplitAttributesCalculatorParams, SplitAttributes> calculator =
+ mController.getSplitAttributesCalculator();
final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
if (calculator == null) {
@@ -538,7 +539,7 @@
final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
taskWindowMetrics, taskConfiguration, defaultSplitAttributes,
isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag());
- final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params);
+ final SplitAttributes splitAttributes = calculator.apply(params);
return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 84b2bfc..32b915c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -44,6 +44,7 @@
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.RawFoldingFeatureProducer;
+import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.util.DataProducer;
import java.util.ArrayList;
@@ -51,7 +52,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Reference implementation of androidx.window.extensions.layout OEM interface for use with
@@ -80,6 +80,10 @@
private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
+ mJavaToExtConsumers = new ArrayMap<>();
+
public WindowLayoutComponentImpl(@NonNull Context context) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
@@ -90,7 +94,8 @@
}
/** Registers to listen to {@link CommonFoldingFeature} changes */
- public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
+ public void addFoldingStateChangedCallback(
+ java.util.function.Consumer<List<CommonFoldingFeature>> consumer) {
synchronized (mLock) {
mFoldingFeatureProducer.addDataChangedCallback(consumer);
}
@@ -104,13 +109,17 @@
*/
@Override
public void addWindowLayoutInfoListener(@NonNull Activity activity,
- @NonNull Consumer<WindowLayoutInfo> consumer) {
- addWindowLayoutInfoListener((Context) activity, consumer);
+ @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+ final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
+ synchronized (mLock) {
+ mJavaToExtConsumers.put(consumer, extConsumer);
+ }
+ addWindowLayoutInfoListener(activity, extConsumer);
}
/**
- * Similar to {@link #addWindowLayoutInfoListener(Activity, Consumer)}, but takes a UI Context
- * as a parameter.
+ * Similar to {@link #addWindowLayoutInfoListener(Activity, java.util.function.Consumer)}, but
+ * takes a UI Context as a parameter.
*
* Jetpack {@link androidx.window.layout.ExtensionWindowLayoutInfoBackend} makes sure all
* consumers related to the same {@link Context} gets updated {@link WindowLayoutInfo}
@@ -151,6 +160,18 @@
}
}
+ @Override
+ public void removeWindowLayoutInfoListener(
+ @NonNull java.util.function.Consumer<WindowLayoutInfo> consumer) {
+ final Consumer<WindowLayoutInfo> extConsumer;
+ synchronized (mLock) {
+ extConsumer = mJavaToExtConsumers.remove(consumer);
+ }
+ if (extConsumer != null) {
+ removeWindowLayoutInfoListener(extConsumer);
+ }
+ }
+
/**
* Removes a listener no longer interested in receiving updates.
*
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index 2f92a57..459ec9f 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -34,9 +34,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Pair;
+import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerToken;
+import androidx.window.extensions.core.util.function.Predicate;
import androidx.window.extensions.embedding.SplitAttributes.SplitType;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -107,7 +109,7 @@
static SplitRule createSplitRule(@NonNull Activity primaryActivity,
@NonNull Intent secondaryIntent, boolean clearTop) {
final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent);
- return new SplitPairRule.Builder(
+ return createSplitPairRuleBuilder(
activityPair -> false,
targetPair::equals,
w -> true)
@@ -144,7 +146,7 @@
@NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
int finishSecondaryWithPrimary, boolean clearTop) {
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
- return new SplitPairRule.Builder(
+ return createSplitPairRuleBuilder(
targetPair::equals,
activityIntentPair -> false,
w -> true)
@@ -223,4 +225,26 @@
displayFeatures.add(foldingFeature);
return new WindowLayoutInfo(displayFeatures);
}
+
+ static ActivityRule.Builder createActivityBuilder(
+ @NonNull Predicate<Activity> activityPredicate,
+ @NonNull Predicate<Intent> intentPredicate) {
+ return new ActivityRule.Builder(activityPredicate, intentPredicate);
+ }
+
+ static SplitPairRule.Builder createSplitPairRuleBuilder(
+ @NonNull Predicate<Pair<Activity, Activity>> activitiesPairPredicate,
+ @NonNull Predicate<Pair<Activity, Intent>> activityIntentPairPredicate,
+ @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+ return new SplitPairRule.Builder(activitiesPairPredicate, activityIntentPairPredicate,
+ windowMetricsPredicate);
+ }
+
+ static SplitPlaceholderRule.Builder createSplitPlaceholderRuleBuilder(
+ @NonNull Intent placeholderIntent, @NonNull Predicate<Activity> activityPredicate,
+ @NonNull Predicate<Intent> intentPredicate,
+ @NonNull Predicate<WindowMetrics> windowMetricsPredicate) {
+ return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
+ intentPredicate, windowMetricsPredicate);
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 3cc31f9..f9cbb5c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -34,8 +34,11 @@
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -429,7 +432,7 @@
@Test
public void testResolveStartActivityIntent_withoutLaunchingActivity() {
final Intent intent = new Intent();
- final ActivityRule expandRule = new ActivityRule.Builder(r -> false, i -> i == intent)
+ final ActivityRule expandRule = createActivityBuilder(r -> false, i -> i == intent)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1167,7 +1170,7 @@
@Test
public void testHasSamePresentation() {
- SplitPairRule splitRule1 = new SplitPairRule.Builder(
+ SplitPairRule splitRule1 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1175,7 +1178,7 @@
.setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
- SplitPairRule splitRule2 = new SplitPairRule.Builder(
+ SplitPairRule splitRule2 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1188,7 +1191,7 @@
SplitController.haveSamePresentation(splitRule1, splitRule2,
new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
- splitRule2 = new SplitPairRule.Builder(
+ splitRule2 = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> true)
@@ -1352,7 +1355,7 @@
/** Setups a rule to always expand the given intent. */
private void setupExpandRule(@NonNull Intent expandIntent) {
- final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals)
+ final ActivityRule expandRule = createActivityBuilder(r -> false, expandIntent::equals)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1360,7 +1363,7 @@
/** Setups a rule to always expand the given activity. */
private void setupExpandRule(@NonNull Activity expandActivity) {
- final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false)
+ final ActivityRule expandRule = createActivityBuilder(expandActivity::equals, i -> false)
.setShouldAlwaysExpand(true)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
@@ -1368,7 +1371,7 @@
/** Setups a rule to launch placeholder for the given activity. */
private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
- final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
+ final SplitRule placeholderRule = createSplitPlaceholderRuleBuilder(PLACEHOLDER_INTENT,
primaryActivity::equals, i -> false, w -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 6dae0a1..cbb6e31 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -27,6 +27,7 @@
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
@@ -473,7 +474,7 @@
final Activity secondaryActivity = createMockActivity();
final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
- final SplitPairRule rule = new SplitPairRule.Builder(pair ->
+ final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
metrics -> true)
.setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
@@ -491,7 +492,7 @@
@Test
public void testComputeSplitAttributes() {
- final SplitPairRule splitPairRule = new SplitPairRule.Builder(
+ final SplitPairRule splitPairRule = createSplitPairRuleBuilder(
activityPair -> true,
activityIntentPair -> true,
windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS))
diff --git a/libs/WindowManager/Jetpack/window-extensions-core-release.aar b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
new file mode 100644
index 0000000..4c08074
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-core-release.aar
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 4978e04..6fd8d29 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b4d5ae..e148d4f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,7 +60,7 @@
// except for SystemUI-core.
// Copied from compose/features/Android.bp.
static_libs: [
- "SystemUIComposeCore",
+ "PlatformComposeCore",
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2f5b42f..810dd33 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -193,7 +193,7 @@
<permission android:name="com.android.systemui.permission.FLAGS"
android:protectionLevel="signature" />
- <permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ <permission android:name="android.permission.CUSTOMIZE_SYSTEM_UI"
android:protectionLevel="signature|privileged" />
<!-- Adding Quick Settings tiles -->
@@ -1003,10 +1003,10 @@
</receiver>
<provider
- android:authorities="com.android.systemui.keyguard.quickaffordance"
- android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ android:authorities="com.android.systemui.customization"
+ android:name="com.android.systemui.keyguard.CustomizationProvider"
android:exported="true"
- android:permission="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
+ android:permission="android.permission.CUSTOMIZE_SYSTEM_UI"
/>
</application>
</manifest>
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index fbdb526..ab3efb8 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -22,7 +22,7 @@
}
android_library {
- name: "SystemUIComposeCore",
+ name: "PlatformComposeCore",
manifest: "AndroidManifest.xml",
srcs: [
diff --git a/packages/SystemUI/compose/core/AndroidManifest.xml b/packages/SystemUI/compose/core/AndroidManifest.xml
index 83c442d..20543db 100644
--- a/packages/SystemUI/compose/core/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.compose.core">
+ package="com.android.compose.core">
</manifest>
diff --git a/packages/SystemUI/compose/core/TEST_MAPPING b/packages/SystemUI/compose/core/TEST_MAPPING
index dc243d2..ee95f73 100644
--- a/packages/SystemUI/compose/core/TEST_MAPPING
+++ b/packages/SystemUI/compose/core/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"presubmit": [
{
- "name": "SystemUIComposeCoreTests",
+ "name": "PlatformComposeCoreTests",
"options": [
{
"exclude-annotation": "org.junit.Ignore"
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
similarity index 94%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index 496f4b3..d1ee18a 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.compose
+package com.android.compose
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues
@@ -27,10 +27,10 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.LocalAndroidColorScheme
@Composable
-fun SysUiButton(
+fun PlatformButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
@@ -48,7 +48,7 @@
}
@Composable
-fun SysUiOutlinedButton(
+fun PlatformOutlinedButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
@@ -67,7 +67,7 @@
}
@Composable
-fun SysUiTextButton(
+fun PlatformTextButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
index c9470c8..a02954a 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/SystemUiController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose
+package com.android.compose
import android.app.Activity
import android.content.Context
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index d31ca51..259f0ed 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.animation
+package com.android.compose.animation
import android.content.Context
import android.view.View
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index f75b3a8..edb10c7d 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.animation
+package com.android.compose.animation
import android.view.View
import android.view.ViewGroup
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
index 79f1cad1..d400f77 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/ViewTreeSavedStateRegistryOwner.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ViewTreeSavedStateRegistryOwner.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.animation
+package com.android.compose.animation
import android.view.View
import androidx.savedstate.SavedStateRegistryOwner
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
index 121bf2c..13c2464 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/FadingBackground.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/FadingBackground.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.DrawModifier
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
index 3b13c0b..d9bc9c9 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Padding.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LayoutModifier
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
index 570d2431..ec1b966 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/modifiers/Size.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.compose.modifiers
+package com.android.compose.modifiers
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.IntrinsicMeasurable
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
index 19624e6..eb9d625 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.DecayAnimationSpec
@@ -40,7 +40,6 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
/** Library-wide switch to turn on debug logging. */
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
index 288c26e..2e6ae78 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
import androidx.annotation.FloatRange
import androidx.annotation.IntRange
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
similarity index 99%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
index 0b53f532..23122de 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.layout.pager
+package com.android.compose.pager
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationState
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt b/packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
index 3f2f96b..228ba3d 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/runtime/MovableContent.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/runtime/MovableContent.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.runtime
+package com.android.compose.runtime
import androidx.compose.runtime.Composable
import androidx.compose.runtime.InternalComposeApi
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
similarity index 96%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
index caa7e5f..4f1657f 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme
+package com.android.compose.theme
import android.annotation.ColorInt
import android.content.Context
@@ -27,7 +27,7 @@
staticCompositionLocalOf<AndroidColorScheme> {
throw IllegalStateException(
"No AndroidColorScheme configured. Make sure to use LocalAndroidColorScheme in a " +
- "Composable surrounded by a SystemUITheme {}."
+ "Composable surrounded by a PlatformTheme {}."
)
}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
similarity index 95%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
index de47cce..5dbaff6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/Color.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme
+package com.android.compose.theme
import android.annotation.AttrRes
import androidx.compose.runtime.Composable
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
similarity index 77%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index 00532f4..b4e90d6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/SystemUITheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme
+package com.android.compose.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
@@ -24,15 +24,15 @@
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
-import com.android.systemui.compose.theme.typography.TypeScaleTokens
-import com.android.systemui.compose.theme.typography.TypefaceNames
-import com.android.systemui.compose.theme.typography.TypefaceTokens
-import com.android.systemui.compose.theme.typography.TypographyTokens
-import com.android.systemui.compose.theme.typography.systemUITypography
+import com.android.compose.theme.typography.TypeScaleTokens
+import com.android.compose.theme.typography.TypefaceNames
+import com.android.compose.theme.typography.TypefaceTokens
+import com.android.compose.theme.typography.TypographyTokens
+import com.android.compose.theme.typography.platformTypography
-/** The Material 3 theme that should wrap all SystemUI Composables. */
+/** The Material 3 theme that should wrap all Platform Composables. */
@Composable
-fun SystemUITheme(
+fun PlatformTheme(
isDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
@@ -49,7 +49,7 @@
val typefaceNames = remember(context) { TypefaceNames.get(context) }
val typography =
remember(typefaceNames) {
- systemUITypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
+ platformTypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
}
MaterialTheme(colorScheme, typography = typography) {
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
similarity index 91%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
index 365f4bb..1ce1ae3 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/SystemUITypography.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/PlatformTypography.kt
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Typography
/**
- * The SystemUI typography.
+ * The typography for Platform Compose code.
*
* Do not use directly and call [MaterialTheme.typography] instead to access the different text
* styles.
*/
-internal fun systemUITypography(typographyTokens: TypographyTokens): Typography {
+internal fun platformTypography(typographyTokens: TypographyTokens): Typography {
return Typography(
displayLarge = typographyTokens.displayLarge,
displayMedium = typographyTokens.displayMedium,
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
index 537ba0b..78fcf5c 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypeScaleTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypeScaleTokens.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
import androidx.compose.ui.unit.sp
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
similarity index 97%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
index f3d554f..13acfd6 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypefaceTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypefaceTokens.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalTextApi::class)
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
import android.content.Context
import androidx.compose.ui.text.ExperimentalTextApi
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
similarity index 98%
rename from packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
index 55f3d1f..38aadb8 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/typography/TypographyTokens.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/typography/TypographyTokens.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme.typography
+package com.android.compose.theme.typography
import androidx.compose.ui.text.TextStyle
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index f8023e2..6119e96 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -23,7 +23,7 @@
// TODO(b/230606318): Make those host tests instead of device tests.
android_test {
- name: "SystemUIComposeCoreTests",
+ name: "PlatformComposeCoreTests",
manifest: "AndroidManifest.xml",
test_suites: ["device-tests"],
sdk_version: "current",
@@ -34,7 +34,7 @@
],
static_libs: [
- "SystemUIComposeCore",
+ "PlatformComposeCore",
"androidx.test.runner",
"androidx.test.ext.junit",
diff --git a/packages/SystemUI/compose/core/tests/AndroidManifest.xml b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
index 729ab98..1016340 100644
--- a/packages/SystemUI/compose/core/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
@@ -15,14 +15,14 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.compose.core.tests" >
+ package="com.android.compose.core.tests" >
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.systemui.compose.core.tests"
- android:label="Tests for SystemUIComposeCore"/>
+ android:targetPackage="com.android.compose.core.tests"
+ android:label="Tests for PlatformComposeCore"/>
</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
similarity index 90%
rename from packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt
rename to packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
index 20249f6..9bc6856 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/systemui/compose/theme/SystemUIThemeTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/theme/SystemUIThemeTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.compose.theme
+package com.android.compose.theme
import androidx.compose.material3.Text
import androidx.compose.ui.test.assertIsDisplayed
@@ -32,7 +32,7 @@
@Test
fun testThemeShowsContent() {
- composeRule.setContent { SystemUITheme { Text("foo") } }
+ composeRule.setContent { PlatformTheme { Text("foo") } }
composeRule.onNodeWithText("foo").assertIsDisplayed()
}
@@ -40,7 +40,7 @@
@Test
fun testAndroidColorsAreAvailableInsideTheme() {
composeRule.setContent {
- SystemUITheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) }
+ PlatformTheme { Text("foo", color = LocalAndroidColorScheme.current.colorAccent) }
}
composeRule.onNodeWithText("foo").assertIsDisplayed()
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 16294d9..6991ff8 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -18,7 +18,7 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
-import com.android.systemui.compose.theme.SystemUITheme
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.people.ui.compose.PeopleScreen
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
@@ -31,6 +31,6 @@
viewModel: PeopleViewModel,
onResult: (PeopleViewModel.Result) -> Unit,
) {
- activity.setContent { SystemUITheme { PeopleScreen(viewModel, onResult) } }
+ activity.setContent { PlatformTheme { PeopleScreen(viewModel, onResult) } }
}
}
diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp
index 4533330..8f8bb1b 100644
--- a/packages/SystemUI/compose/features/Android.bp
+++ b/packages/SystemUI/compose/features/Android.bp
@@ -31,7 +31,7 @@
static_libs: [
"SystemUI-core",
- "SystemUIComposeCore",
+ "PlatformComposeCore",
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 4a56b02..23dacf9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -49,8 +49,8 @@
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.R
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index 5c9358f..3f590df 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -41,8 +41,8 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.R
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
@Composable
internal fun PeopleScreenEmpty(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 654b723..5c5ceef 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -65,14 +65,14 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.background
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.compose.theme.colorAttr
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.compose.animation.Expandable
-import com.android.systemui.compose.modifiers.background
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.compose.theme.colorAttr
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
index 2fa475d..2f41ea9 100644
--- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
@@ -35,7 +35,7 @@
android:enabled="false"
tools:replace="android:authorities"
tools:node="remove" />
- <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
android:enabled="false"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/compose/testing/Android.bp b/packages/SystemUI/compose/testing/Android.bp
index 293e51f..555f42e 100644
--- a/packages/SystemUI/compose/testing/Android.bp
+++ b/packages/SystemUI/compose/testing/Android.bp
@@ -30,7 +30,7 @@
],
static_libs: [
- "SystemUIComposeCore",
+ "PlatformComposeCore",
"SystemUIScreenshotLib",
"androidx.compose.runtime_runtime",
diff --git a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
index 979e1a0..cb9e53c 100644
--- a/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
+++ b/packages/SystemUI/compose/testing/src/com/android/systemui/testing/compose/ComposeScreenshotTestRule.kt
@@ -22,7 +22,7 @@
import androidx.compose.ui.platform.ViewRootForTest
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
-import com.android.systemui.compose.theme.SystemUITheme
+import com.android.compose.theme.PlatformTheme
import com.android.systemui.testing.screenshot.ScreenshotActivity
import com.android.systemui.testing.screenshot.SystemUIGoldenImagePathManager
import com.android.systemui.testing.screenshot.UnitTestBitmapMatcher
@@ -79,7 +79,7 @@
// Set the content using the AndroidComposeRule to make sure that the Activity is set up
// correctly.
composeRule.setContent {
- SystemUITheme {
+ PlatformTheme {
Surface(
color = MaterialTheme.colorScheme.background,
) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
similarity index 75%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
index 3213b2e..5bb3707 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.shared.quickaffordance.data.content
+package com.android.systemui.shared.customization.data.content
import android.annotation.SuppressLint
import android.content.ContentValues
@@ -25,7 +25,7 @@
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.annotation.DrawableRes
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -35,7 +35,7 @@
import kotlinx.coroutines.withContext
/** Client for using a content provider implementing the [Contract]. */
-interface KeyguardQuickAffordanceProviderClient {
+interface CustomizationProviderClient {
/**
* Selects an affordance with the given ID for a slot on the lock screen with the given ID.
@@ -190,10 +190,10 @@
)
}
-class KeyguardQuickAffordanceProviderClientImpl(
+class CustomizationProviderClientImpl(
private val context: Context,
private val backgroundDispatcher: CoroutineDispatcher,
-) : KeyguardQuickAffordanceProviderClient {
+) : CustomizationProviderClient {
override suspend fun insertSelection(
slotId: String,
@@ -201,20 +201,23 @@
) {
withContext(backgroundDispatcher) {
context.contentResolver.insert(
- Contract.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
ContentValues().apply {
- put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
- put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+ put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
+ put(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+ affordanceId
+ )
}
)
}
}
- override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+ override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
- Contract.SlotTable.URI,
+ Contract.LockScreenQuickAffordances.SlotTable.URI,
null,
null,
null,
@@ -222,16 +225,21 @@
)
?.use { cursor ->
buildList {
- val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+ val idColumnIndex =
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
+ )
val capacityColumnIndex =
- cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
+ )
if (idColumnIndex == -1 || capacityColumnIndex == -1) {
return@buildList
}
while (cursor.moveToNext()) {
add(
- KeyguardQuickAffordanceProviderClient.Slot(
+ CustomizationProviderClient.Slot(
id = cursor.getString(idColumnIndex),
capacity = cursor.getInt(capacityColumnIndex),
)
@@ -243,7 +251,7 @@
?: emptyList()
}
- override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+ override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
@@ -265,7 +273,7 @@
while (cursor.moveToNext()) {
add(
- KeyguardQuickAffordanceProviderClient.Flag(
+ CustomizationProviderClient.Flag(
name = cursor.getString(nameColumnIndex),
value = cursor.getInt(valueColumnIndex) == 1,
)
@@ -277,20 +285,19 @@
?: emptyList()
}
- override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
- return observeUri(Contract.SlotTable.URI).map { querySlots() }
+ override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
+ return observeUri(Contract.LockScreenQuickAffordances.SlotTable.URI).map { querySlots() }
}
- override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+ override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
}
- override suspend fun queryAffordances():
- List<KeyguardQuickAffordanceProviderClient.Affordance> {
+ override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
- Contract.AffordanceTable.URI,
+ Contract.LockScreenQuickAffordances.AffordanceTable.URI,
null,
null,
null,
@@ -299,24 +306,36 @@
?.use { cursor ->
buildList {
val idColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
+ )
val nameColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
+ )
val iconColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
+ )
val isEnabledColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.IS_ENABLED)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .IS_ENABLED
+ )
val enablementInstructionsColumnIndex =
cursor.getColumnIndex(
- Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_INSTRUCTIONS
)
val enablementActionTextColumnIndex =
cursor.getColumnIndex(
- Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_ACTION_TEXT
)
val enablementComponentNameColumnIndex =
cursor.getColumnIndex(
- Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_COMPONENT_NAME
)
if (
idColumnIndex == -1 ||
@@ -332,7 +351,7 @@
while (cursor.moveToNext()) {
add(
- KeyguardQuickAffordanceProviderClient.Affordance(
+ CustomizationProviderClient.Affordance(
id = cursor.getString(idColumnIndex),
name = cursor.getString(nameColumnIndex),
iconResourceId = cursor.getInt(iconColumnIndex),
@@ -341,7 +360,7 @@
cursor
.getString(enablementInstructionsColumnIndex)
?.split(
- Contract.AffordanceTable
+ Contract.LockScreenQuickAffordances.AffordanceTable
.ENABLEMENT_INSTRUCTIONS_DELIMITER
),
enablementActionText =
@@ -357,16 +376,17 @@
?: emptyList()
}
- override fun observeAffordances():
- Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
- return observeUri(Contract.AffordanceTable.URI).map { queryAffordances() }
+ override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
+ return observeUri(Contract.LockScreenQuickAffordances.AffordanceTable.URI).map {
+ queryAffordances()
+ }
}
- override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
- Contract.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
null,
null,
null,
@@ -375,11 +395,19 @@
?.use { cursor ->
buildList {
val slotIdColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
+ )
val affordanceIdColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns
+ .AFFORDANCE_ID
+ )
val affordanceNameColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns
+ .AFFORDANCE_NAME
+ )
if (
slotIdColumnIndex == -1 ||
affordanceIdColumnIndex == -1 ||
@@ -390,7 +418,7 @@
while (cursor.moveToNext()) {
add(
- KeyguardQuickAffordanceProviderClient.Selection(
+ CustomizationProviderClient.Selection(
slotId = cursor.getString(slotIdColumnIndex),
affordanceId = cursor.getString(affordanceIdColumnIndex),
affordanceName = cursor.getString(affordanceNameColumnIndex),
@@ -403,8 +431,10 @@
?: emptyList()
}
- override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
- return observeUri(Contract.SelectionTable.URI).map { querySelections() }
+ override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
+ return observeUri(Contract.LockScreenQuickAffordances.SelectionTable.URI).map {
+ querySelections()
+ }
}
override suspend fun deleteSelection(
@@ -413,9 +443,10 @@
) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
- Contract.SelectionTable.URI,
- "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
- " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
+ "${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
+ " ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
+ " = ?",
arrayOf(
slotId,
affordanceId,
@@ -429,8 +460,8 @@
) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
- Contract.SelectionTable.URI,
- Contract.SelectionTable.Columns.SLOT_ID,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
arrayOf(
slotId,
),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
new file mode 100644
index 0000000..1e2e7d2
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.shared.customization.data.content
+
+import android.content.ContentResolver
+import android.net.Uri
+
+/** Contract definitions for querying content about keyguard quick affordances. */
+object CustomizationProviderContract {
+
+ const val AUTHORITY = "com.android.systemui.customization"
+ const val PERMISSION = "android.permission.CUSTOMIZE_SYSTEM_UI"
+
+ private val BASE_URI: Uri =
+ Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
+
+ /** Namespace for lock screen shortcut (quick affordance) tables. */
+ object LockScreenQuickAffordances {
+
+ const val NAMESPACE = "lockscreen_quickaffordance"
+
+ private val LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI: Uri =
+ BASE_URI.buildUpon().path(NAMESPACE).build()
+
+ fun qualifiedTablePath(tableName: String): String {
+ return "$NAMESPACE/$tableName"
+ }
+
+ /**
+ * Table for slots.
+ *
+ * Slots are positions where affordances can be placed on the lock screen. Affordances that
+ * are placed on slots are said to be "selected". The system supports the idea of multiple
+ * affordances per slot, though the implementation may limit the number of affordances on
+ * each slot.
+ *
+ * Supported operations:
+ * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result
+ * set will contain rows with the [SlotTable.Columns] columns.
+ */
+ object SlotTable {
+ const val TABLE_NAME = "slots"
+ val URI: Uri =
+ LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for this slot. */
+ const val ID = "id"
+ /** Integer. The maximum number of affordances that can be placed in the slot. */
+ const val CAPACITY = "capacity"
+ }
+ }
+
+ /**
+ * Table for affordances.
+ *
+ * Affordances are actions/buttons that the user can execute. They are placed on slots on
+ * the lock screen.
+ *
+ * Supported operations:
+ * - Query - to know about all the affordances that are available on the device, regardless
+ * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result
+ * set will contain rows, each with the columns specified in [AffordanceTable.Columns].
+ */
+ object AffordanceTable {
+ const val TABLE_NAME = "affordances"
+ val URI: Uri =
+ LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+ const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
+ const val COMPONENT_NAME_SEPARATOR = "/"
+
+ object Columns {
+ /** String. Unique ID for this affordance. */
+ const val ID = "id"
+ /** String. User-visible name for this affordance. */
+ const val NAME = "name"
+ /**
+ * Integer. Resource ID for the drawable to load for this affordance. This is a
+ * resource ID from the system UI package.
+ */
+ const val ICON = "icon"
+ /** Integer. `1` if the affordance is enabled or `0` if it disabled. */
+ const val IS_ENABLED = "is_enabled"
+ /**
+ * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be
+ * shown to the user if the affordance is disabled and the user selects the
+ * affordance.
+ */
+ const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
+ /**
+ * String. Optional label for a button that, when clicked, opens a destination
+ * activity where the user can re-enable the disabled affordance.
+ */
+ const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
+ /**
+ * String. Optional package name and activity action string, delimited by
+ * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that
+ * opens a destination where the user can re-enable the disabled affordance.
+ */
+ const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
+ }
+ }
+
+ /**
+ * Table for selections.
+ *
+ * Selections are pairs of slot and affordance IDs.
+ *
+ * Supported operations:
+ * - Insert - to insert an affordance and place it in a slot, insert values for the columns
+ * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
+ * Selecting a new affordance for a slot that is already full will automatically remove the
+ * oldest affordance from the slot.
+ * - Query - to know which affordances are set on which slots, query the
+ * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the
+ * columns from [SelectionTable.Columns].
+ * - Delete - to unselect an affordance, removing it from a slot, delete from the
+ * [SelectionTable.URI] [Uri], passing in values for each column.
+ */
+ object SelectionTable {
+ const val TABLE_NAME = "selections"
+ val URI: Uri =
+ LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
+
+ object Columns {
+ /** String. Unique ID for the slot. */
+ const val SLOT_ID = "slot_id"
+ /** String. Unique ID for the selected affordance. */
+ const val AFFORDANCE_ID = "affordance_id"
+ /** String. Human-readable name for the affordance. */
+ const val AFFORDANCE_NAME = "affordance_name"
+ }
+ }
+ }
+
+ /**
+ * Table for flags.
+ *
+ * Flags are key-value pairs.
+ *
+ * Supported operations:
+ * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
+ * contain rows, each of which with the columns from [FlagsTable.Columns].
+ */
+ object FlagsTable {
+ const val TABLE_NAME = "flags"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
+ const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
+
+ /**
+ * Flag denoting whether the customizable lock screen quick affordances feature is enabled.
+ */
+ const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
+ "is_custom_lock_screen_quick_affordances_feature_enabled"
+
+ /** Flag denoting whether the customizable clocks feature is enabled. */
+ const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
+
+ object Columns {
+ /** String. Unique ID for the flag. */
+ const val NAME = "name"
+ /** Int. Value of the flag. `1` means `true` and `0` means `false`. */
+ const val VALUE = "value"
+ }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
similarity index 71%
rename from packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
index ec5e703..f5a955d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/FakeKeyguardQuickAffordanceProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
@@ -15,7 +15,7 @@
*
*/
-package com.android.systemui.shared.quickaffordance.data.content
+package com.android.systemui.shared.customization.data.content
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
@@ -25,46 +25,46 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
-class FakeKeyguardQuickAffordanceProviderClient(
- slots: List<KeyguardQuickAffordanceProviderClient.Slot> =
+class FakeCustomizationProviderClient(
+ slots: List<CustomizationProviderClient.Slot> =
listOf(
- KeyguardQuickAffordanceProviderClient.Slot(
+ CustomizationProviderClient.Slot(
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
capacity = 1,
),
- KeyguardQuickAffordanceProviderClient.Slot(
+ CustomizationProviderClient.Slot(
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
capacity = 1,
),
),
- affordances: List<KeyguardQuickAffordanceProviderClient.Affordance> =
+ affordances: List<CustomizationProviderClient.Affordance> =
listOf(
- KeyguardQuickAffordanceProviderClient.Affordance(
+ CustomizationProviderClient.Affordance(
id = AFFORDANCE_1,
name = AFFORDANCE_1,
iconResourceId = 1,
),
- KeyguardQuickAffordanceProviderClient.Affordance(
+ CustomizationProviderClient.Affordance(
id = AFFORDANCE_2,
name = AFFORDANCE_2,
iconResourceId = 2,
),
- KeyguardQuickAffordanceProviderClient.Affordance(
+ CustomizationProviderClient.Affordance(
id = AFFORDANCE_3,
name = AFFORDANCE_3,
iconResourceId = 3,
),
),
- flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
+ flags: List<CustomizationProviderClient.Flag> =
listOf(
- KeyguardQuickAffordanceProviderClient.Flag(
+ CustomizationProviderClient.Flag(
name =
- KeyguardQuickAffordanceProviderContract.FlagsTable
+ CustomizationProviderContract.FlagsTable
.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value = true,
)
),
-) : KeyguardQuickAffordanceProviderClient {
+) : CustomizationProviderClient {
private val slots = MutableStateFlow(slots)
private val affordances = MutableStateFlow(affordances)
@@ -85,37 +85,35 @@
selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
}
- override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
+ override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
return slots.value
}
- override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
+ override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
return flags.value
}
- override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
+ override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
return slots.asStateFlow()
}
- override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
+ override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
return flags.asStateFlow()
}
- override suspend fun queryAffordances():
- List<KeyguardQuickAffordanceProviderClient.Affordance> {
+ override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return affordances.value
}
- override fun observeAffordances():
- Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
+ override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
return affordances.asStateFlow()
}
- override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
return toSelectionList(selections.value, affordances.value)
}
- override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
+ override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
return combine(selections, affordances) { selections, affordances ->
toSelectionList(selections, affordances)
}
@@ -148,7 +146,7 @@
flags.value =
flags.value.toMutableList().apply {
removeIf { it.name == name }
- add(KeyguardQuickAffordanceProviderClient.Flag(name = name, value = value))
+ add(CustomizationProviderClient.Flag(name = name, value = value))
}
}
@@ -157,29 +155,26 @@
slots.value.toMutableList().apply {
val index = indexOfFirst { it.id == slotId }
check(index != -1) { "Slot with ID \"$slotId\" doesn't exist!" }
- set(
- index,
- KeyguardQuickAffordanceProviderClient.Slot(id = slotId, capacity = capacity)
- )
+ set(index, CustomizationProviderClient.Slot(id = slotId, capacity = capacity))
}
}
- fun addAffordance(affordance: KeyguardQuickAffordanceProviderClient.Affordance): Int {
+ fun addAffordance(affordance: CustomizationProviderClient.Affordance): Int {
affordances.value = affordances.value + listOf(affordance)
return affordances.value.size - 1
}
private fun toSelectionList(
selections: Map<String, List<String>>,
- affordances: List<KeyguardQuickAffordanceProviderClient.Affordance>,
- ): List<KeyguardQuickAffordanceProviderClient.Selection> {
+ affordances: List<CustomizationProviderClient.Affordance>,
+ ): List<CustomizationProviderClient.Selection> {
return selections
.map { (slotId, affordanceIds) ->
affordanceIds.map { affordanceId ->
val affordanceName =
affordances.find { it.id == affordanceId }?.name
?: error("No affordance with ID of \"$affordanceId\"!")
- KeyguardQuickAffordanceProviderClient.Selection(
+ CustomizationProviderClient.Selection(
slotId = slotId,
affordanceId = affordanceId,
affordanceName = affordanceName,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
deleted file mode 100644
index e197752..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/data/content/KeyguardQuickAffordanceProviderContract.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2022 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.shared.quickaffordance.data.content
-
-import android.content.ContentResolver
-import android.net.Uri
-
-/** Contract definitions for querying content about keyguard quick affordances. */
-object KeyguardQuickAffordanceProviderContract {
-
- const val AUTHORITY = "com.android.systemui.keyguard.quickaffordance"
- const val PERMISSION = "android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
-
- private val BASE_URI: Uri =
- Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
-
- /**
- * Table for slots.
- *
- * Slots are positions where affordances can be placed on the lock screen. Affordances that are
- * placed on slots are said to be "selected". The system supports the idea of multiple
- * affordances per slot, though the implementation may limit the number of affordances on each
- * slot.
- *
- * Supported operations:
- * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result set
- * will contain rows with the [SlotTable.Columns] columns.
- */
- object SlotTable {
- const val TABLE_NAME = "slots"
- val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
- object Columns {
- /** String. Unique ID for this slot. */
- const val ID = "id"
- /** Integer. The maximum number of affordances that can be placed in the slot. */
- const val CAPACITY = "capacity"
- }
- }
-
- /**
- * Table for affordances.
- *
- * Affordances are actions/buttons that the user can execute. They are placed on slots on the
- * lock screen.
- *
- * Supported operations:
- * - Query - to know about all the affordances that are available on the device, regardless of
- * which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result set will
- * contain rows, each with the columns specified in [AffordanceTable.Columns].
- */
- object AffordanceTable {
- const val TABLE_NAME = "affordances"
- val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
- const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
- const val COMPONENT_NAME_SEPARATOR = "/"
-
- object Columns {
- /** String. Unique ID for this affordance. */
- const val ID = "id"
- /** String. User-visible name for this affordance. */
- const val NAME = "name"
- /**
- * Integer. Resource ID for the drawable to load for this affordance. This is a resource
- * ID from the system UI package.
- */
- const val ICON = "icon"
- /** Integer. `1` if the affordance is enabled or `0` if it disabled. */
- const val IS_ENABLED = "is_enabled"
- /**
- * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be shown
- * to the user if the affordance is disabled and the user selects the affordance. The
- * first one is a title while the rest are the steps needed to re-enable the affordance.
- */
- const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
- /**
- * String. Optional label for a button that, when clicked, opens a destination activity
- * where the user can re-enable the disabled affordance.
- */
- const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
- /**
- * String. Optional package name and activity action string, delimited by
- * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that opens a
- * destination where the user can re-enable the disabled affordance.
- */
- const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
- }
- }
-
- /**
- * Table for selections.
- *
- * Selections are pairs of slot and affordance IDs.
- *
- * Supported operations:
- * - Insert - to insert an affordance and place it in a slot, insert values for the columns into
- * the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
- * Selecting a new affordance for a slot that is already full will automatically remove the
- * oldest affordance from the slot.
- * - Query - to know which affordances are set on which slots, query the [SelectionTable.URI]
- * [Uri]. The result set will contain rows, each of which with the columns from
- * [SelectionTable.Columns].
- * - Delete - to unselect an affordance, removing it from a slot, delete from the
- * [SelectionTable.URI] [Uri], passing in values for each column.
- */
- object SelectionTable {
- const val TABLE_NAME = "selections"
- val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
- object Columns {
- /** String. Unique ID for the slot. */
- const val SLOT_ID = "slot_id"
- /** String. Unique ID for the selected affordance. */
- const val AFFORDANCE_ID = "affordance_id"
- /** String. Human-readable name for the affordance. */
- const val AFFORDANCE_NAME = "affordance_name"
- }
- }
-
- /**
- * Table for flags.
- *
- * Flags are key-value pairs.
- *
- * Supported operations:
- * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
- * contain rows, each of which with the columns from [FlagsTable.Columns].
- */
- object FlagsTable {
- const val TABLE_NAME = "flags"
- val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
-
- /** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
- const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
-
- /**
- * Flag denoting whether the customizable lock screen quick affordances feature is enabled.
- */
- const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
- "is_custom_lock_screen_quick_affordances_feature_enabled"
-
- /** Flag denoting whether the customizable clocks feature is enabled. */
- const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
-
- object Columns {
- /** String. Unique ID for the flag. */
- const val NAME = "name"
- /** Int. Value of the flag. `1` means `true` and `0` means `false`. */
- const val VALUE = "value"
- }
- }
-}
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index 01d4f00..ccb35fa 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -31,7 +31,7 @@
### Accessing Quick Affordance Data
Quick Affordances structured data are exposed to other applications through the `KeyguardQuickAffordanceProvider` content provider which is owned by the System UI process.
-To access this content provider, applications must have the `android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
+To access this content provider, applications must have the `android.permission.CUSTOMIZE_SYSTEM_UI` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
Generally speaking, there are three important tables served by the content provider: `slots`, `affordances`, and `selections`. There is also a `flags` table, but that's not important and may be ignored.
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index b75c5c7..434f227 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -460,7 +460,7 @@
-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/data/repository/WifiRepository.kt
+-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.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
@@ -752,7 +752,7 @@
-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/WifiRepositoryImplTest.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
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 29369cd..200c478 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2707,12 +2707,6 @@
<string name="keyguard_affordance_enablement_dialog_action_template">Open <xliff:g id="appName" example="Wallet">%1$s</xliff:g></string>
<!--
- Template for a message shown right before a list of instructions that tell the user what to do
- in order to enable a shortcut to a specific app. [CHAR LIMIT=NONE]
- -->
- <string name="keyguard_affordance_enablement_dialog_message">To add the <xliff:g id="appName" example="Wallet">%1$s</xliff:g> app as a shortcut, make sure</string>
-
- <!--
Requirement for the wallet app to be available for the user to use. This is shown as part of a
bulleted list of requirements. When all requirements are met, the app can be accessed through a
shortcut button on the lock screen. [CHAR LIMIT=NONE].
@@ -2748,10 +2742,10 @@
<string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string>
<!--
- Error message shown when a button should be pressed and held to activate it, usually shown when
- the user attempted to tap the button or held it for too short a time. [CHAR LIMIT=32].
+ Error message shown when a shortcut must be pressed and held to activate it, usually shown when
+ the user tried to tap the shortcut or held it for too short a time. [CHAR LIMIT=32].
-->
- <string name="keyguard_affordance_press_too_short">Touch & hold to open</string>
+ <string name="keyguard_affordance_press_too_short">Touch & hold shortcut</string>
<!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] -->
<string name="rear_display_bottom_sheet_cancel">Cancel</string>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index b30e0c2..a431a59 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -16,7 +16,7 @@
package com.android.systemui.dagger;
-import com.android.systemui.keyguard.KeyguardQuickAffordanceProvider;
+import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
@@ -49,5 +49,5 @@
/**
* Member injection into the supplied argument.
*/
- void inject(KeyguardQuickAffordanceProvider keyguardQuickAffordanceProvider);
+ void inject(CustomizationProvider customizationProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
rename to packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index cbcede0..eaf1081 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -33,11 +33,11 @@
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
-class KeyguardQuickAffordanceProvider :
+class CustomizationProvider :
ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
@Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
@@ -49,17 +49,23 @@
UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(
Contract.AUTHORITY,
- Contract.SlotTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+ ),
MATCH_CODE_ALL_SLOTS,
)
addURI(
Contract.AUTHORITY,
- Contract.AffordanceTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+ ),
MATCH_CODE_ALL_AFFORDANCES,
)
addURI(
Contract.AUTHORITY,
- Contract.SelectionTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+ ),
MATCH_CODE_ALL_SELECTIONS,
)
addURI(
@@ -94,9 +100,18 @@
val tableName =
when (uriMatcher.match(uri)) {
- MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
- MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
- MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
+ MATCH_CODE_ALL_SLOTS ->
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+ )
+ MATCH_CODE_ALL_AFFORDANCES ->
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+ )
+ MATCH_CODE_ALL_SELECTIONS ->
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+ )
MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
else -> null
}
@@ -174,22 +189,34 @@
throw IllegalArgumentException("Cannot insert selection, no values passed in!")
}
- if (!values.containsKey(Contract.SelectionTable.Columns.SLOT_ID)) {
+ if (
+ !values.containsKey(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
+ ) {
throw IllegalArgumentException(
"Cannot insert selection, " +
- "\"${Contract.SelectionTable.Columns.SLOT_ID}\" not specified!"
+ "\"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID}\"" +
+ " not specified!"
)
}
- if (!values.containsKey(Contract.SelectionTable.Columns.AFFORDANCE_ID)) {
+ if (
+ !values.containsKey(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+ )
+ ) {
throw IllegalArgumentException(
"Cannot insert selection, " +
- "\"${Contract.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
+ "\"${Contract.LockScreenQuickAffordances
+ .SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
)
}
- val slotId = values.getAsString(Contract.SelectionTable.Columns.SLOT_ID)
- val affordanceId = values.getAsString(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+ val slotId =
+ values.getAsString(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
+ val affordanceId =
+ values.getAsString(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+ )
if (slotId.isNullOrEmpty()) {
throw IllegalArgumentException("Cannot insert selection, slot ID was empty!")
@@ -207,8 +234,10 @@
return if (success) {
Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
- context?.contentResolver?.notifyChange(Contract.SelectionTable.URI, null)
- Contract.SelectionTable.URI
+ context
+ ?.contentResolver
+ ?.notifyChange(Contract.LockScreenQuickAffordances.SelectionTable.URI, null)
+ Contract.LockScreenQuickAffordances.SelectionTable.URI
} else {
Log.d(TAG, "Failed to select $affordanceId for slot $slotId")
null
@@ -218,9 +247,9 @@
private suspend fun querySelections(): Cursor {
return MatrixCursor(
arrayOf(
- Contract.SelectionTable.Columns.SLOT_ID,
- Contract.SelectionTable.Columns.AFFORDANCE_ID,
- Contract.SelectionTable.Columns.AFFORDANCE_NAME,
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_NAME,
)
)
.apply {
@@ -243,13 +272,16 @@
private suspend fun queryAffordances(): Cursor {
return MatrixCursor(
arrayOf(
- Contract.AffordanceTable.Columns.ID,
- Contract.AffordanceTable.Columns.NAME,
- Contract.AffordanceTable.Columns.ICON,
- Contract.AffordanceTable.Columns.IS_ENABLED,
- Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS,
- Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT,
- Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.IS_ENABLED,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_INSTRUCTIONS,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_ACTION_TEXT,
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns
+ .ENABLEMENT_COMPONENT_NAME,
)
)
.apply {
@@ -261,7 +293,8 @@
representation.iconResourceId,
if (representation.isEnabled) 1 else 0,
representation.instructions?.joinToString(
- Contract.AffordanceTable.ENABLEMENT_INSTRUCTIONS_DELIMITER
+ Contract.LockScreenQuickAffordances.AffordanceTable
+ .ENABLEMENT_INSTRUCTIONS_DELIMITER
),
representation.actionText,
representation.actionComponentName,
@@ -274,8 +307,8 @@
private fun querySlots(): Cursor {
return MatrixCursor(
arrayOf(
- Contract.SlotTable.Columns.ID,
- Contract.SlotTable.Columns.CAPACITY,
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY,
)
)
.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 394426d..09e5ec0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -81,10 +81,6 @@
instructions =
listOf(
context.getString(
- R.string.keyguard_affordance_enablement_dialog_message,
- pickerName,
- ),
- context.getString(
R.string.keyguard_affordance_enablement_dialog_home_instruction_1
),
context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 02ebcd3..20588e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -22,7 +22,7 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
@@ -90,8 +90,9 @@
* the user to be able to set up the quick affordance and make it enabled.
*
* This is either just an action for the `Intent` or a package name and action,
- * separated by [Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR] for convenience, you
- * can use the [componentName] function.
+ * separated by
+ * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for
+ * convenience, you can use the [componentName] function.
*/
val actionComponentName: String? = null,
) : PickerScreenState() {
@@ -145,8 +146,8 @@
/**
* Returning this as a result from the [onTriggered] method means that the implementation
- * has _not_ taken care of the action and the system should show a Dialog using the
- * given [AlertDialog] and [Expandable].
+ * has _not_ taken care of the action and the system should show a Dialog using the given
+ * [AlertDialog] and [Expandable].
*/
data class ShowDialog(
val dialog: AlertDialog,
@@ -162,7 +163,8 @@
return when {
action.isNullOrEmpty() -> null
!packageName.isNullOrEmpty() ->
- "$packageName${Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR}$action"
+ "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable
+ .COMPONENT_NAME_SEPARATOR}$action"
else -> action
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
index 727a813..60ef116 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactory.kt
@@ -19,13 +19,13 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
interface KeyguardQuickAffordanceProviderClientFactory {
- fun create(): KeyguardQuickAffordanceProviderClient
+ fun create(): CustomizationProviderClient
}
class KeyguardQuickAffordanceProviderClientFactoryImpl
@@ -34,8 +34,8 @@
private val userTracker: UserTracker,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceProviderClientFactory {
- override fun create(): KeyguardQuickAffordanceProviderClient {
- return KeyguardQuickAffordanceProviderClientImpl(
+ override fun create(): CustomizationProviderClient {
+ return CustomizationProviderClientImpl(
context = userTracker.userContext,
backgroundDispatcher = backgroundDispatcher,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
index 8ffef25..e9bd889 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -24,7 +24,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -69,7 +69,7 @@
awaitClose { userTracker.removeCallback(callback) }
}
- private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
+ private val clientOrNull: StateFlow<CustomizationProviderClient?> =
userId
.distinctUntilChanged()
.map { selectedUserId ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index a96ce77..4f7990f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -84,10 +84,6 @@
instructions =
listOf(
context.getString(
- R.string.keyguard_affordance_enablement_dialog_message,
- pickerName,
- ),
- context.getString(
R.string
.keyguard_affordance_enablement_dialog_qr_scanner_instruction
),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index beb20ce..1928f40 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -118,10 +118,6 @@
instructions =
listOf(
context.getString(
- R.string.keyguard_affordance_enablement_dialog_message,
- pickerName,
- ),
- context.getString(
R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1
),
context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 9772cb9..c219380 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -36,7 +36,7 @@
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index b19795c..0e4058b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -392,6 +392,7 @@
}
view.performClick()
view.setOnClickListener(null)
+ cancel()
}
true
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
index 1aa954f..012b9ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
@@ -51,6 +51,16 @@
*/
val operatorAlphaShort: String? = null,
+ /**
+ * TODO (b/263167683): Clarify this field
+ *
+ * This check comes from [com.android.settingslib.Utils.isInService]. It is intended to be a
+ * mapping from a ServiceState to a notion of connectivity. Notably, it will consider a
+ * connection to be in-service if either the voice registration state is IN_SERVICE or the data
+ * registration state is IN_SERVICE and NOT IWLAN.
+ */
+ val isInService: Boolean = false,
+
/** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
val isGsm: Boolean = false,
@IntRange(from = 0, to = 4)
@@ -99,6 +109,10 @@
row.logChange(COL_OPERATOR, operatorAlphaShort)
}
+ if (prevVal.isInService != isInService) {
+ row.logChange(COL_IS_IN_SERVICE, isInService)
+ }
+
if (prevVal.isGsm != isGsm) {
row.logChange(COL_IS_GSM, isGsm)
}
@@ -129,6 +143,7 @@
row.logChange(COL_EMERGENCY, isEmergencyOnly)
row.logChange(COL_ROAMING, isRoaming)
row.logChange(COL_OPERATOR, operatorAlphaShort)
+ row.logChange(COL_IS_IN_SERVICE, isInService)
row.logChange(COL_IS_GSM, isGsm)
row.logChange(COL_CDMA_LEVEL, cdmaLevel)
row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
@@ -141,6 +156,7 @@
const val COL_EMERGENCY = "EmergencyOnly"
const val COL_ROAMING = "Roaming"
const val COL_OPERATOR = "OperatorName"
+ const val COL_IS_IN_SERVICE = "IsInService"
const val COL_IS_GSM = "IsGsm"
const val COL_CDMA_LEVEL = "CdmaLevel"
const val COL_PRIMARY_LEVEL = "PrimaryLevel"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index b252de8..0b5f9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -247,6 +247,7 @@
return MobileConnectionModel(
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
isRoaming = roaming,
+ isInService = (level ?: 0) > 0,
isGsm = false, // TODO(b/261029387): not yet supported
cdmaLevel = level ?: 0,
primaryLevel = level ?: 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 0b9e158..5cfff82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -32,6 +32,7 @@
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
+import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
@@ -117,6 +118,7 @@
isEmergencyOnly = serviceState.isEmergencyOnly,
isRoaming = serviceState.roaming,
operatorAlphaShort = serviceState.operatorAlphaShort,
+ isInService = Utils.isInService(serviceState),
)
trySend(state)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index e6686dc..31ac7a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -48,6 +48,9 @@
/** True when telephony tells us that the data state is CONNECTED */
val isDataConnected: StateFlow<Boolean>
+ /** True if we consider this connection to be in service, i.e. can make calls */
+ val isInService: StateFlow<Boolean>
+
// TODO(b/256839546): clarify naming of default vs active
/** True if we want to consider the data connection enabled */
val isDefaultDataEnabled: StateFlow<Boolean>
@@ -58,6 +61,9 @@
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
+ /** True if the CDMA level should be preferred over the primary level. */
+ val alwaysUseCdmaLevel: StateFlow<Boolean>
+
/** Observable for RAT type (network type) indicator */
val networkTypeIconGroup: StateFlow<MobileIconGroup>
@@ -94,6 +100,7 @@
@Application scope: CoroutineScope,
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
+ override val alwaysUseCdmaLevel: StateFlow<Boolean>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
override val isDefaultConnectionFailed: StateFlow<Boolean>,
@@ -154,13 +161,12 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val level: StateFlow<Int> =
- connectionInfo
- .mapLatest { connection ->
- // TODO: incorporate [MobileMappings.Config.alwaysShowCdmaRssi]
- if (connection.isGsm) {
- connection.primaryLevel
- } else {
- connection.cdmaLevel
+ combine(connectionInfo, alwaysUseCdmaLevel) { connection, alwaysUseCdmaLevel ->
+ when {
+ // GSM connections should never use the CDMA level
+ connection.isGsm -> connection.primaryLevel
+ alwaysUseCdmaLevel -> connection.cdmaLevel
+ else -> connection.primaryLevel
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
@@ -175,4 +181,9 @@
connectionInfo
.mapLatest { connection -> connection.dataConnectionState == Connected }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ override val isInService =
+ connectionRepository.connectionInfo
+ .mapLatest { it.isInService }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
}
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 21f6d8e..83da1dd 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
@@ -55,8 +55,13 @@
val filteredSubscriptions: Flow<List<SubscriptionModel>>
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
+
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
+
+ /** True if the CDMA level should be preferred over the primary level. */
+ val alwaysUseCdmaLevel: StateFlow<Boolean>
+
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
@@ -165,6 +170,11 @@
.mapLatest { it.alwaysShowDataRatIcon }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val alwaysUseCdmaLevel: StateFlow<Boolean> =
+ mobileConnectionsRepo.defaultDataSubRatConfig
+ .mapLatest { it.alwaysShowCdmaRssi }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
/** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */
override val defaultMobileIconGroup: StateFlow<MobileIconGroup> =
mobileConnectionsRepo.defaultMobileIconGroup.stateIn(
@@ -196,6 +206,7 @@
scope,
activeDataConnectionHasDataEnabled,
alwaysShowDataRatIcon,
+ alwaysUseCdmaLevel,
defaultMobileIconMapping,
defaultMobileIconGroup,
isDefaultConnectionFailed,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 2d6ac4e..a2117c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -80,11 +80,17 @@
override val iconId: Flow<Int> = run {
val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
- combine(iconInteractor.level, iconInteractor.numberOfLevels, showExclamationMark) {
- level,
- numberOfLevels,
- showExclamationMark ->
- SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+ combine(
+ iconInteractor.level,
+ iconInteractor.numberOfLevels,
+ showExclamationMark,
+ iconInteractor.isInService,
+ ) { level, numberOfLevels, showExclamationMark, isInService ->
+ if (!isInService) {
+ SignalDrawable.getEmptyState(numberOfLevels)
+ } else {
+ SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+ }
}
.distinctUntilChanged()
.logDiffsForTable(
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 5ccd6f4..53525f2 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
@@ -16,51 +16,9 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository
-import android.annotation.SuppressLint
-import android.content.IntentFilter
-import android.net.ConnectivityManager
-import android.net.Network
-import android.net.NetworkCapabilities
-import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
-import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
-import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
-import android.net.NetworkCapabilities.TRANSPORT_WIFI
-import android.net.NetworkRequest
-import android.net.wifi.WifiInfo
-import android.net.wifi.WifiManager
-import android.net.wifi.WifiManager.TrafficStateCallback
-import android.util.Log
-import com.android.settingslib.Utils
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
-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 java.util.concurrent.Executor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
/** Provides data related to the wifi state. */
interface WifiRepository {
@@ -76,250 +34,3 @@
/** Observable for the current wifi network activity. */
val wifiActivity: StateFlow<DataActivityModel>
}
-
-/** Real implementation of [WifiRepository]. */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
-@SuppressLint("MissingPermission")
-class WifiRepositoryImpl @Inject constructor(
- broadcastDispatcher: BroadcastDispatcher,
- connectivityManager: ConnectivityManager,
- logger: ConnectivityPipelineLogger,
- @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
- @Main mainExecutor: Executor,
- @Application scope: CoroutineScope,
- wifiManager: WifiManager?,
-) : WifiRepository {
-
- private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
- IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
- )
- .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
-
- private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
- MutableSharedFlow(extraBufferCapacity = 1)
-
- override val isWifiEnabled: StateFlow<Boolean> =
- if (wifiManager == null) {
- MutableStateFlow(false).asStateFlow()
- } else {
- // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
- // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
- // have changed.
- merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
- .mapLatest { wifiManager.isWifiEnabled }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "",
- columnName = "isWifiEnabled",
- initialValue = wifiManager.isWifiEnabled,
- )
- .stateIn(
- scope = scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = wifiManager.isWifiEnabled
- )
- }
-
- override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
- // Note: This callback doesn't do any logging because we already log every network change
- // in the [wifiNetwork] callback.
- val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
- override fun onCapabilitiesChanged(
- network: Network,
- networkCapabilities: NetworkCapabilities
- ) {
- // This method will always be called immediately after the network becomes the
- // default, in addition to any time the capabilities change while the network is
- // the default.
- // If this network contains valid wifi info, then wifi is the default network.
- val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
- trySend(wifiInfo != null)
- }
-
- override fun onLost(network: Network) {
- // The system no longer has a default network, so wifi is definitely not default.
- trySend(false)
- }
- }
-
- connectivityManager.registerDefaultNetworkCallback(callback)
- awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "",
- columnName = "isWifiDefault",
- initialValue = false,
- )
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false
- )
-
- override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
- var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
-
- val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
- override fun onCapabilitiesChanged(
- network: Network,
- networkCapabilities: NetworkCapabilities
- ) {
- logger.logOnCapabilitiesChanged(network, networkCapabilities)
-
- wifiNetworkChangeEvents.tryEmit(Unit)
-
- val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
- if (wifiInfo?.isPrimary == true) {
- val wifiNetworkModel = createWifiNetworkModel(
- wifiInfo,
- network,
- networkCapabilities,
- wifiManager,
- )
- logger.logTransformation(
- WIFI_NETWORK_CALLBACK_NAME,
- oldValue = currentWifi,
- newValue = wifiNetworkModel
- )
- currentWifi = wifiNetworkModel
- trySend(wifiNetworkModel)
- }
- }
-
- override fun onLost(network: Network) {
- logger.logOnLost(network)
-
- wifiNetworkChangeEvents.tryEmit(Unit)
-
- val wifi = currentWifi
- if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
- val newNetworkModel = WifiNetworkModel.Inactive
- logger.logTransformation(
- WIFI_NETWORK_CALLBACK_NAME,
- oldValue = wifi,
- newValue = newNetworkModel
- )
- currentWifi = newNetworkModel
- trySend(newNetworkModel)
- }
- }
- }
-
- connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
-
- awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = "wifiNetwork",
- initialValue = WIFI_NETWORK_DEFAULT,
- )
- // There will be multiple wifi icons in different places that will frequently
- // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
- // new subscribes will get the latest value immediately upon subscription. Otherwise, the
- // views could show stale data. See b/244173280.
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = WIFI_NETWORK_DEFAULT
- )
-
- override val wifiActivity: StateFlow<DataActivityModel> =
- if (wifiManager == null) {
- Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
- flowOf(ACTIVITY_DEFAULT)
- } else {
- conflatedCallbackFlow {
- val callback = TrafficStateCallback { state ->
- logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
- trySend(state.toWifiDataActivityModel())
- }
- wifiManager.registerTrafficStateCallback(mainExecutor, callback)
- awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
- }
- }
- .logDiffsForTable(
- wifiTableLogBuffer,
- columnPrefix = ACTIVITY_PREFIX,
- initialValue = ACTIVITY_DEFAULT,
- )
- .stateIn(
- scope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = ACTIVITY_DEFAULT
- )
-
- companion object {
- private const val ACTIVITY_PREFIX = "wifiActivity"
-
- val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
- // Start out with no known wifi network.
- // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
- // initial fetch to get a starting wifi network. But, it uses a deprecated API
- // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
- // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
- // NetworkCallback inside [wifiNetwork] for our wifi network information.
- val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
-
- private fun networkCapabilitiesToWifiInfo(
- networkCapabilities: NetworkCapabilities
- ): WifiInfo? {
- return when {
- networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
- networkCapabilities.transportInfo as WifiInfo?
- networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
- // Sometimes, cellular networks can act as wifi networks (known as VCN --
- // virtual carrier network). So, see if this cellular network has wifi info.
- Utils.tryGetWifiInfoForVcn(networkCapabilities)
- else -> null
- }
- }
-
- private fun createWifiNetworkModel(
- wifiInfo: WifiInfo,
- network: Network,
- networkCapabilities: NetworkCapabilities,
- wifiManager: WifiManager?,
- ): WifiNetworkModel {
- return if (wifiInfo.isCarrierMerged) {
- WifiNetworkModel.CarrierMerged
- } else {
- WifiNetworkModel.Active(
- network.getNetId(),
- isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
- level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
- wifiInfo.ssid,
- wifiInfo.isPasspointAp,
- wifiInfo.isOsuAp,
- wifiInfo.passpointProviderFriendlyName
- )
- }
- }
-
- private fun prettyPrintActivity(activity: Int): String {
- return when (activity) {
- TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
- TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
- TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
- TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
- else -> "INVALID"
- }
- }
-
- private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
- NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NET_CAPABILITY_NOT_VPN)
- .addTransportType(TRANSPORT_WIFI)
- .addTransportType(TRANSPORT_CELLULAR)
- .build()
-
- private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
- }
-}
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 73bcdfd..be86620 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
@@ -25,6 +25,7 @@
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 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/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
new file mode 100644
index 0000000..c8c94e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 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.pipeline.wifi.data.repository.prod
+
+import android.annotation.SuppressLint
+import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN
+import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import android.net.wifi.WifiInfo
+import android.net.wifi.WifiManager
+import android.net.wifi.WifiManager.TrafficStateCallback
+import android.util.Log
+import com.android.settingslib.Utils
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
+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.WifiRepository
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.stateIn
+
+/** Real implementation of [WifiRepository]. */
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+@SuppressLint("MissingPermission")
+class WifiRepositoryImpl @Inject constructor(
+ broadcastDispatcher: BroadcastDispatcher,
+ connectivityManager: ConnectivityManager,
+ logger: ConnectivityPipelineLogger,
+ @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
+ @Main mainExecutor: Executor,
+ @Application scope: CoroutineScope,
+ wifiManager: WifiManager?,
+) : WifiRepository {
+
+ private val wifiStateChangeEvents: Flow<Unit> = broadcastDispatcher.broadcastFlow(
+ IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)
+ )
+ .logInputChange(logger, "WIFI_STATE_CHANGED_ACTION intent")
+
+ private val wifiNetworkChangeEvents: MutableSharedFlow<Unit> =
+ MutableSharedFlow(extraBufferCapacity = 1)
+
+ override val isWifiEnabled: StateFlow<Boolean> =
+ if (wifiManager == null) {
+ MutableStateFlow(false).asStateFlow()
+ } else {
+ // Because [WifiManager] doesn't expose a wifi enabled change listener, we do it
+ // internally by fetching [WifiManager.isWifiEnabled] whenever we think the state may
+ // have changed.
+ merge(wifiNetworkChangeEvents, wifiStateChangeEvents)
+ .mapLatest { wifiManager.isWifiEnabled }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiEnabled",
+ initialValue = wifiManager.isWifiEnabled,
+ )
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = wifiManager.isWifiEnabled
+ )
+ }
+
+ override val isWifiDefault: StateFlow<Boolean> = conflatedCallbackFlow {
+ // Note: This callback doesn't do any logging because we already log every network change
+ // in the [wifiNetwork] callback.
+ val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities
+ ) {
+ // This method will always be called immediately after the network becomes the
+ // default, in addition to any time the capabilities change while the network is
+ // the default.
+ // If this network contains valid wifi info, then wifi is the default network.
+ val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+ trySend(wifiInfo != null)
+ }
+
+ override fun onLost(network: Network) {
+ // The system no longer has a default network, so wifi is definitely not default.
+ trySend(false)
+ }
+ }
+
+ connectivityManager.registerDefaultNetworkCallback(callback)
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ columnName = "isWifiDefault",
+ initialValue = false,
+ )
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
+
+ override val wifiNetwork: StateFlow<WifiNetworkModel> = conflatedCallbackFlow {
+ var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
+
+ val callback = object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
+ override fun onCapabilitiesChanged(
+ network: Network,
+ networkCapabilities: NetworkCapabilities
+ ) {
+ logger.logOnCapabilitiesChanged(network, networkCapabilities)
+
+ wifiNetworkChangeEvents.tryEmit(Unit)
+
+ val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
+ if (wifiInfo?.isPrimary == true) {
+ val wifiNetworkModel = createWifiNetworkModel(
+ wifiInfo,
+ network,
+ networkCapabilities,
+ wifiManager,
+ )
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = currentWifi,
+ newValue = wifiNetworkModel
+ )
+ currentWifi = wifiNetworkModel
+ trySend(wifiNetworkModel)
+ }
+ }
+
+ override fun onLost(network: Network) {
+ logger.logOnLost(network)
+
+ wifiNetworkChangeEvents.tryEmit(Unit)
+
+ val wifi = currentWifi
+ if (wifi is WifiNetworkModel.Active && wifi.networkId == network.getNetId()) {
+ val newNetworkModel = WifiNetworkModel.Inactive
+ logger.logTransformation(
+ WIFI_NETWORK_CALLBACK_NAME,
+ oldValue = wifi,
+ newValue = newNetworkModel
+ )
+ currentWifi = newNetworkModel
+ trySend(newNetworkModel)
+ }
+ }
+ }
+
+ connectivityManager.registerNetworkCallback(WIFI_NETWORK_CALLBACK_REQUEST, callback)
+
+ awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "wifiNetwork",
+ initialValue = WIFI_NETWORK_DEFAULT,
+ )
+ // There will be multiple wifi icons in different places that will frequently
+ // subscribe/unsubscribe to flows as the views attach/detach. Using [stateIn] ensures that
+ // new subscribes will get the latest value immediately upon subscription. Otherwise, the
+ // views could show stale data. See b/244173280.
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = WIFI_NETWORK_DEFAULT
+ )
+
+ override val wifiActivity: StateFlow<DataActivityModel> =
+ if (wifiManager == null) {
+ Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
+ flowOf(ACTIVITY_DEFAULT)
+ } else {
+ conflatedCallbackFlow {
+ val callback = TrafficStateCallback { state ->
+ logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
+ trySend(state.toWifiDataActivityModel())
+ }
+ wifiManager.registerTrafficStateCallback(mainExecutor, callback)
+ awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
+ }
+ }
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = ACTIVITY_PREFIX,
+ initialValue = ACTIVITY_DEFAULT,
+ )
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = ACTIVITY_DEFAULT
+ )
+
+ companion object {
+ private const val ACTIVITY_PREFIX = "wifiActivity"
+
+ val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ // Start out with no known wifi network.
+ // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
+ // initial fetch to get a starting wifi network. But, it uses a deprecated API
+ // [WifiManager.getConnectionInfo()], and the deprecation doc indicates to just use
+ // [ConnectivityManager.NetworkCallback] results instead. So, for now we'll just rely on the
+ // NetworkCallback inside [wifiNetwork] for our wifi network information.
+ val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
+
+ private fun networkCapabilitiesToWifiInfo(
+ networkCapabilities: NetworkCapabilities
+ ): WifiInfo? {
+ return when {
+ networkCapabilities.hasTransport(TRANSPORT_WIFI) ->
+ networkCapabilities.transportInfo as WifiInfo?
+ networkCapabilities.hasTransport(TRANSPORT_CELLULAR) ->
+ // Sometimes, cellular networks can act as wifi networks (known as VCN --
+ // virtual carrier network). So, see if this cellular network has wifi info.
+ Utils.tryGetWifiInfoForVcn(networkCapabilities)
+ else -> null
+ }
+ }
+
+ private fun createWifiNetworkModel(
+ wifiInfo: WifiInfo,
+ network: Network,
+ networkCapabilities: NetworkCapabilities,
+ wifiManager: WifiManager?,
+ ): WifiNetworkModel {
+ return if (wifiInfo.isCarrierMerged) {
+ WifiNetworkModel.CarrierMerged
+ } else {
+ WifiNetworkModel.Active(
+ network.getNetId(),
+ isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED),
+ level = wifiManager?.calculateSignalLevel(wifiInfo.rssi),
+ wifiInfo.ssid,
+ wifiInfo.isPasspointAp,
+ wifiInfo.isOsuAp,
+ wifiInfo.passpointProviderFriendlyName
+ )
+ }
+ }
+
+ private fun prettyPrintActivity(activity: Int): String {
+ return when (activity) {
+ TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
+ TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
+ TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
+ TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
+ else -> "INVALID"
+ }
+ }
+
+ private val WIFI_NETWORK_CALLBACK_REQUEST: NetworkRequest =
+ NetworkRequest.Builder()
+ .clearCapabilities()
+ .addCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_WIFI)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build()
+
+ private const val WIFI_NETWORK_CALLBACK_NAME = "wifiNetworkModel"
+ }
+}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index d8331ab..d4e06bc 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -146,7 +146,7 @@
tools:replace="android:authorities"
tools:node="remove" />
- <provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
+ <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
android:enabled="false"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 5e4a43f..4659766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -49,8 +49,8 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
@@ -75,7 +75,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
+class CustomizationProviderTest : SysuiTestCase() {
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -87,7 +87,7 @@
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- private lateinit var underTest: KeyguardQuickAffordanceProvider
+ private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
@@ -98,7 +98,7 @@
whenever(previewRendererFactory.create(any())).thenReturn(previewRenderer)
whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
- underTest = KeyguardQuickAffordanceProvider()
+ underTest = CustomizationProvider()
val testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
val localUserSelectionManager =
@@ -205,19 +205,34 @@
@Test
fun getType() {
- assertThat(underTest.getType(Contract.AffordanceTable.URI))
+ assertThat(underTest.getType(Contract.LockScreenQuickAffordances.AffordanceTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd." +
- "${Contract.AUTHORITY}.${Contract.AffordanceTable.TABLE_NAME}"
+ "${Contract.AUTHORITY}." +
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
+ )
)
- assertThat(underTest.getType(Contract.SlotTable.URI))
+ assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SlotTable.URI))
.isEqualTo(
- "vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}.${Contract.SlotTable.TABLE_NAME}"
+ "vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}." +
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
+ )
)
- assertThat(underTest.getType(Contract.SelectionTable.URI))
+ assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SelectionTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd." +
- "${Contract.AUTHORITY}.${Contract.SelectionTable.TABLE_NAME}"
+ "${Contract.AUTHORITY}." +
+ Contract.LockScreenQuickAffordances.qualifiedTablePath(
+ Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
+ )
+ )
+ assertThat(underTest.getType(Contract.FlagsTable.URI))
+ .isEqualTo(
+ "vnd.android.cursor.dir/vnd." +
+ "${Contract.AUTHORITY}." +
+ Contract.FlagsTable.TABLE_NAME
)
}
@@ -296,9 +311,10 @@
)
context.contentResolver.delete(
- Contract.SelectionTable.URI,
- "${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
- " ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
+ "${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
+ " ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
+ " = ?",
arrayOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
AFFORDANCE_2,
@@ -330,8 +346,8 @@
)
context.contentResolver.delete(
- Contract.SelectionTable.URI,
- Contract.SelectionTable.Columns.SLOT_ID,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
arrayOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
),
@@ -371,10 +387,13 @@
affordanceId: String,
) {
context.contentResolver.insert(
- Contract.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
ContentValues().apply {
- put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
- put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
+ put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
+ put(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
+ affordanceId
+ )
}
)
}
@@ -382,7 +401,7 @@
private fun querySelections(): List<Selection> {
return context.contentResolver
.query(
- Contract.SelectionTable.URI,
+ Contract.LockScreenQuickAffordances.SelectionTable.URI,
null,
null,
null,
@@ -391,11 +410,18 @@
?.use { cursor ->
buildList {
val slotIdColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
+ )
val affordanceIdColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
+ )
val affordanceNameColumnIndex =
- cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SelectionTable.Columns
+ .AFFORDANCE_NAME
+ )
if (
slotIdColumnIndex == -1 ||
affordanceIdColumnIndex == -1 ||
@@ -421,7 +447,7 @@
private fun querySlots(): List<Slot> {
return context.contentResolver
.query(
- Contract.SlotTable.URI,
+ Contract.LockScreenQuickAffordances.SlotTable.URI,
null,
null,
null,
@@ -429,9 +455,14 @@
)
?.use { cursor ->
buildList {
- val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
+ val idColumnIndex =
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
+ )
val capacityColumnIndex =
- cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
+ )
if (idColumnIndex == -1 || capacityColumnIndex == -1) {
return@buildList
}
@@ -452,7 +483,7 @@
private fun queryAffordances(): List<Affordance> {
return context.contentResolver
.query(
- Contract.AffordanceTable.URI,
+ Contract.LockScreenQuickAffordances.AffordanceTable.URI,
null,
null,
null,
@@ -460,11 +491,18 @@
)
?.use { cursor ->
buildList {
- val idColumnIndex = cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
+ val idColumnIndex =
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
+ )
val nameColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
+ )
val iconColumnIndex =
- cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
+ cursor.getColumnIndex(
+ Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
+ )
if (idColumnIndex == -1 || nameColumnIndex == -1 || iconColumnIndex == -1) {
return@buildList
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
index d7e9cf1..b21cec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManagerTest.kt
@@ -22,8 +22,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -54,16 +54,16 @@
private lateinit var testScope: TestScope
private lateinit var testDispatcher: TestDispatcher
private lateinit var userTracker: FakeUserTracker
- private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
- private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client1: FakeCustomizationProviderClient
+ private lateinit var client2: FakeCustomizationProviderClient
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
whenever(userHandle.isSystem).thenReturn(true)
- client1 = FakeKeyguardQuickAffordanceProviderClient()
- client2 = FakeKeyguardQuickAffordanceProviderClient()
+ client1 = FakeCustomizationProviderClient()
+ client2 = FakeCustomizationProviderClient()
userTracker = FakeUserTracker()
userTracker.set(
@@ -122,11 +122,11 @@
client1.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
- affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
)
client2.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
- affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
)
userTracker.set(
@@ -139,7 +139,7 @@
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
listOf(
- FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ FakeCustomizationProviderClient.AFFORDANCE_1,
),
)
)
@@ -154,7 +154,7 @@
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
listOf(
- FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ FakeCustomizationProviderClient.AFFORDANCE_2,
),
)
)
@@ -174,7 +174,7 @@
client1.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
- affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
)
userTracker.set(
userInfos = userTracker.userProfiles,
@@ -197,7 +197,7 @@
underTest.setSelections(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
- affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
+ affordanceIds = listOf(FakeCustomizationProviderClient.AFFORDANCE_1),
)
runCurrent()
@@ -206,7 +206,7 @@
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
listOf(
- FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
+ FakeCustomizationProviderClient.AFFORDANCE_1,
),
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index c187a3f..b071a02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -32,8 +32,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -63,19 +63,13 @@
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
private lateinit var config2: FakeKeyguardQuickAffordanceConfig
private lateinit var userTracker: FakeUserTracker
- private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
- private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
+ private lateinit var client1: FakeCustomizationProviderClient
+ private lateinit var client2: FakeCustomizationProviderClient
@Before
fun setUp() {
- config1 =
- FakeKeyguardQuickAffordanceConfig(
- FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
- )
- config2 =
- FakeKeyguardQuickAffordanceConfig(
- FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
- )
+ config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
+ config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
val scope = CoroutineScope(IMMEDIATE)
userTracker = FakeUserTracker()
val localUserSelectionManager =
@@ -95,8 +89,8 @@
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
- client1 = FakeKeyguardQuickAffordanceProviderClient()
- client2 = FakeKeyguardQuickAffordanceProviderClient()
+ client1 = FakeCustomizationProviderClient()
+ client2 = FakeCustomizationProviderClient()
val remoteUserSelectionManager =
KeyguardQuickAffordanceRemoteUserSelectionManager(
scope = scope,
@@ -256,7 +250,7 @@
)
client2.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
- affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
+ affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
)
val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 7970443..c63dd2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -20,7 +20,10 @@
import android.os.UserHandle
import android.provider.Settings
import android.telephony.CellSignalStrengthCdma
+import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState
+import android.telephony.ServiceState.STATE_IN_SERVICE
+import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
import android.telephony.SignalStrength
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyCallback
@@ -47,7 +50,6 @@
import android.telephony.TelephonyManager.EXTRA_SPN
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
-import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
@@ -302,7 +304,6 @@
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
- val type = NETWORK_TYPE_UNKNOWN
val expected = UnknownNetworkType
assertThat(latest?.resolvedNetworkType).isEqualTo(expected)
@@ -590,6 +591,56 @@
job.cancel()
}
+ @Test
+ fun `connection model - isInService - not iwlan`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+ val serviceState = ServiceState()
+ serviceState.voiceRegState = STATE_IN_SERVICE
+ serviceState.dataRegState = STATE_IN_SERVICE
+
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+
+ assertThat(latest).isTrue()
+
+ serviceState.voiceRegState = STATE_OUT_OF_SERVICE
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isTrue()
+
+ serviceState.dataRegState = STATE_OUT_OF_SERVICE
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun `connection model - isInService - is iwlan - voice out of service - data in service`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.connectionInfo.onEach { latest = it.isInService }.launchIn(this)
+
+ // Mock the service state here so we can make it specifically IWLAN
+ val serviceState: ServiceState = mock()
+ whenever(serviceState.state).thenReturn(STATE_OUT_OF_SERVICE)
+ whenever(serviceState.dataRegistrationState).thenReturn(STATE_IN_SERVICE)
+
+ // See [com.android.settingslib.Utils.isInService] for more info. This is one way to
+ // make the network look like IWLAN
+ val networkRegWlan: NetworkRegistrationInfo = mock()
+ whenever(serviceState.getNetworkRegistrationInfo(any(), any()))
+ .thenReturn(networkRegWlan)
+ whenever(networkRegWlan.registrationState)
+ .thenReturn(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+
+ getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
private fun getTelephonyCallbacks(): List<TelephonyCallback> {
val callbackCaptor = argumentCaptor<TelephonyCallback>()
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index c494589..ff72715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -29,6 +29,8 @@
) : MobileIconInteractor {
override val alwaysShowDataRatIcon = MutableStateFlow(false)
+ override val alwaysUseCdmaLevel = MutableStateFlow(false)
+
override val activity =
MutableStateFlow(
DataActivityModel(
@@ -52,6 +54,8 @@
override val isDataConnected = MutableStateFlow(true)
+ override val isInService = MutableStateFlow(true)
+
private val _isDataEnabled = MutableStateFlow(true)
override val isDataEnabled = _isDataEnabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 19e5516..1c00646 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@
override val alwaysShowDataRatIcon = MutableStateFlow(false)
+ override val alwaysUseCdmaLevel = MutableStateFlow(false)
+
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
override val defaultMobileIconMapping = _defaultMobileIconMapping
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 83c5055..5abe335 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -61,6 +61,7 @@
scope,
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
mobileIconsInteractor.alwaysShowDataRatIcon,
+ mobileIconsInteractor.alwaysUseCdmaLevel,
mobileIconsInteractor.defaultMobileIconMapping,
mobileIconsInteractor.defaultMobileIconGroup,
mobileIconsInteractor.isDefaultConnectionFailed,
@@ -103,7 +104,27 @@
}
@Test
- fun cdma_level_default_unknown() =
+ fun gsm_alwaysShowCdmaTrue_stillUsesGsmLevel() =
+ runBlocking(IMMEDIATE) {
+ connectionRepository.setConnectionInfo(
+ MobileConnectionModel(
+ isGsm = true,
+ primaryLevel = GSM_LEVEL,
+ cdmaLevel = CDMA_LEVEL,
+ ),
+ )
+ mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+
+ var latest: Int? = null
+ val job = underTest.level.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(GSM_LEVEL)
+
+ job.cancel()
+ }
+
+ @Test
+ fun notGsm_level_default_unknown() =
runBlocking(IMMEDIATE) {
connectionRepository.setConnectionInfo(
MobileConnectionModel(isGsm = false),
@@ -117,7 +138,7 @@
}
@Test
- fun cdma_usesCdmaLevel() =
+ fun notGsm_alwaysShowCdmaTrue_usesCdmaLevel() =
runBlocking(IMMEDIATE) {
connectionRepository.setConnectionInfo(
MobileConnectionModel(
@@ -126,6 +147,7 @@
cdmaLevel = CDMA_LEVEL
),
)
+ mobileIconsInteractor.alwaysUseCdmaLevel.value = true
var latest: Int? = null
val job = underTest.level.onEach { latest = it }.launchIn(this)
@@ -136,6 +158,26 @@
}
@Test
+ fun notGsm_alwaysShowCdmaFalse_usesPrimaryLevel() =
+ runBlocking(IMMEDIATE) {
+ connectionRepository.setConnectionInfo(
+ MobileConnectionModel(
+ isGsm = false,
+ primaryLevel = GSM_LEVEL,
+ cdmaLevel = CDMA_LEVEL,
+ ),
+ )
+ mobileIconsInteractor.alwaysUseCdmaLevel.value = false
+
+ var latest: Int? = null
+ val job = underTest.level.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isEqualTo(GSM_LEVEL)
+
+ job.cancel()
+ }
+
+ @Test
fun iconGroup_three_g() =
runBlocking(IMMEDIATE) {
connectionRepository.setConnectionInfo(
@@ -240,6 +282,21 @@
}
@Test
+ fun alwaysUseCdmaLevel_matchesParent() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+ mobileIconsInteractor.alwaysUseCdmaLevel.value = true
+ assertThat(latest).isTrue()
+
+ mobileIconsInteractor.alwaysUseCdmaLevel.value = false
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun test_isDefaultDataEnabled_matchesParent() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
@@ -300,6 +357,23 @@
}
@Test
+ fun `isInService - uses repository value`() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isInService.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = true))
+
+ assertThat(latest).isTrue()
+
+ connectionRepository.setConnectionInfo(MobileConnectionModel(isInService = false))
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
fun `roaming - is gsm - uses connection model`() =
runBlocking(IMMEDIATE) {
var latest: Boolean? = null
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 2fa3467..b82a584 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
@@ -291,6 +291,38 @@
job.cancel()
}
+ @Test
+ fun alwaysUseCdmaLevel_configHasTrue() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+ val config = MobileMappings.Config()
+ config.alwaysShowCdmaRssi = true
+ connectionsRepository.defaultDataSubRatConfig.value = config
+ yield()
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun alwaysUseCdmaLevel_configHasFalse() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+
+ val config = MobileMappings.Config()
+ config.alwaysShowCdmaRssi = false
+ connectionsRepository.defaultDataSubRatConfig.value = config
+ yield()
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
private val tableLogBuffer =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 50221bc..2a8d42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -98,6 +98,30 @@
}
@Test
+ fun `icon - uses empty state - when not in service`() =
+ testScope.runTest {
+ var latest: Int? = null
+ val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+
+ interactor.isInService.value = false
+
+ var expected = emptySignal()
+
+ assertThat(latest).isEqualTo(expected)
+
+ // Changing the level doesn't overwrite the disabled state
+ interactor.level.value = 2
+ assertThat(latest).isEqualTo(expected)
+
+ // Once back in service, the regular icon appears
+ interactor.isInService.value = true
+ expected = defaultSignal(level = 2)
+ assertThat(latest).isEqualTo(expected)
+
+ job.cancel()
+ }
+
+ @Test
fun networkType_dataEnabled_groupIsRepresented() =
testScope.runTest {
val expected =
@@ -375,5 +399,7 @@
): Int {
return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
}
+
+ fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
}
}
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 4e15b4a..f5837d6 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
@@ -18,7 +18,7 @@
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.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
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/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index b935442..1085c2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 5d0d87b..befb290 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.wifi.data.repository
+package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
import android.net.ConnectivityManager
import android.net.Network
@@ -33,8 +33,8 @@
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.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
index d85dd2e..16a1298 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceProviderClientFactory.kt
@@ -18,17 +18,17 @@
package com.android.systemui.keyguard.data.quickaffordance
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
-import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
+import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
class FakeKeyguardQuickAffordanceProviderClientFactory(
private val userTracker: UserTracker,
- private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
- FakeKeyguardQuickAffordanceProviderClient()
+ private val callback: (Int) -> CustomizationProviderClient = {
+ FakeCustomizationProviderClient()
},
) : KeyguardQuickAffordanceProviderClientFactory {
- override fun create(): KeyguardQuickAffordanceProviderClient {
+ override fun create(): CustomizationProviderClient {
return callback(userTracker.userId)
}
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index d4ef638..658e38b 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -668,8 +668,8 @@
mIProximityUpdateCallback = new IProximityUpdateCallback.Stub() {
@Override
public void onProximityUpdate(double distance) {
+ mCallbackInternal.onProximityUpdate(distance);
synchronized (mLock) {
- mCallbackInternal.onProximityUpdate(distance);
freeIfInactiveLocked();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f38633f..c712167 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -10127,8 +10127,8 @@
// TODO(b/263592337): Explore enabling compat fake focus for fullscreen, e.g. for when
// covered with bubbles.
boolean shouldSendCompatFakeFocus() {
- return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled() && inMultiWindowMode()
- && !inPinnedWindowingMode() && !inFreeformWindowingMode();
+ return mWmService.mLetterboxConfiguration.isCompatFakeFocusEnabled(info)
+ && inMultiWindowMode() && !inPinnedWindowingMode() && !inFreeformWindowingMode();
}
static class Builder {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 03c5589..9b84233 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -23,6 +23,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.graphics.Color;
import android.provider.DeviceConfig;
import android.util.Slog;
@@ -39,6 +41,10 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM;
+ @VisibleForTesting
+ static final String DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS =
+ "enable_compat_fake_focus";
+
/**
* Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
* set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -108,6 +114,12 @@
/** Letterboxed app window is aligned to the right side. */
static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
+ @VisibleForTesting
+ static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN = "com.android.COMPAT_FAKE_FOCUS_OPT_IN";
+ @VisibleForTesting
+ static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT =
+ "com.android.COMPAT_FAKE_FOCUS_OPT_OUT";
+
final Context mContext;
// Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
@@ -977,11 +989,49 @@
"enable_translucent_activity_letterbox", false);
}
- // TODO(b/262866240): Add listener to check for device config property
+ @VisibleForTesting
+ boolean getPackageManagerProperty(PackageManager pm, String property) {
+ boolean enabled = false;
+ try {
+ final PackageManager.Property p = pm.getProperty(property, mContext.getPackageName());
+ enabled = p.getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ // Property not found
+ }
+ return enabled;
+ }
+
+ @VisibleForTesting
+ boolean isCompatFakeFocusEnabled(ActivityInfo info) {
+ if (!isCompatFakeFocusEnabledOnDevice()) {
+ return false;
+ }
+ // See if the developer has chosen to opt in / out of treatment
+ PackageManager pm = mContext.getPackageManager();
+ if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT)) {
+ return false;
+ } else if (getPackageManagerProperty(pm, PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN)) {
+ return true;
+ }
+ if (info.isChangeEnabled(ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS)) {
+ return true;
+ }
+ return false;
+ }
+
/** Whether fake sending focus is enabled for unfocused apps in splitscreen */
- boolean isCompatFakeFocusEnabled() {
- return mIsCompatFakeFocusEnabled && DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER, "enable_compat_fake_focus", true);
+ boolean isCompatFakeFocusEnabledOnDevice() {
+ return mIsCompatFakeFocusEnabled
+ && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, true);
+ }
+
+ /**
+ * Overrides whether fake sending focus is enabled for unfocused apps in splitscreen
+ */
+ @VisibleForTesting
+ void setIsCompatFakeFocusEnabled(boolean enabled) {
+ mIsCompatFakeFocusEnabled = enabled;
}
/** Whether camera compatibility treatment is enabled. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index e196704..ead1a86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -18,6 +18,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.wm.LetterboxConfiguration.DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
@@ -25,6 +26,8 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -34,6 +37,7 @@
import android.content.Context;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
@@ -43,18 +47,25 @@
import java.util.Arrays;
import java.util.function.BiConsumer;
+/**
+ * Tests for the {@link LetterboxConfiguration} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:LetterboxConfigurationTests
+ */
@SmallTest
@Presubmit
public class LetterboxConfigurationTest {
+ private Context mContext;
private LetterboxConfiguration mLetterboxConfiguration;
private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
@Before
public void setUp() throws Exception {
- Context context = getInstrumentation().getTargetContext();
+ mContext = getInstrumentation().getTargetContext();
mLetterboxConfigurationPersister = mock(LetterboxConfigurationPersister.class);
- mLetterboxConfiguration = new LetterboxConfiguration(context,
+ mLetterboxConfiguration = new LetterboxConfiguration(mContext,
mLetterboxConfigurationPersister);
}
@@ -222,6 +233,34 @@
LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop);
}
+ @Test
+ public void testIsCompatFakeFocusEnabledOnDevice() {
+ boolean wasFakeFocusEnabled = DeviceConfig
+ .getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, false);
+
+ // Set runtime flag to true and build time flag to false
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
+ mLetterboxConfiguration.setIsCompatFakeFocusEnabled(false);
+ assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+ // Set runtime flag to false and build time flag to true
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "false", false);
+ mLetterboxConfiguration.setIsCompatFakeFocusEnabled(true);
+ assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+ // Set runtime flag to true so that both are enabled
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
+ assertTrue(mLetterboxConfiguration.isCompatFakeFocusEnabledOnDevice());
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, Boolean.toString(wasFakeFocusEnabled),
+ false);
+ }
+
private void assertForHorizontalMove(int from, int expected, int expectedTime,
boolean halfFoldPose, BiConsumer<LetterboxConfiguration, Boolean> move) {
// We are in the current position
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7488e1c..e9080ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -53,6 +53,8 @@
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN;
+import static com.android.server.wm.LetterboxConfiguration.PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -3228,6 +3230,80 @@
assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
}
+ private ActivityRecord setUpActivityForCompatFakeFocusTest() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setOnTop(true)
+ // Set the component to be that of the test class in order to enable compat changes
+ .setComponent(ComponentName.createRelative(mContext,
+ com.android.server.wm.SizeCompatTests.class.getName()))
+ .build();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ spyOn(activity.mWmService.mLetterboxConfiguration);
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .isCompatFakeFocusEnabledOnDevice();
+ return activity;
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+ public void testShouldSendFakeFocus_overrideEnabled_returnsTrue() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+
+ assertTrue(activity.shouldSendCompatFakeFocus());
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+ public void testShouldSendFakeFocus_overrideDisabled_returnsFalse() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+
+ assertFalse(activity.shouldSendCompatFakeFocus());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+ public void testIsCompatFakeFocusEnabled_optOutPropertyAndOverrideEnabled_fakeFocusDisabled() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
+
+ assertFalse(activity.mWmService.mLetterboxConfiguration
+ .isCompatFakeFocusEnabled(activity.info));
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS})
+ public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_noOverride_fakeFocusEnabled() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
+
+ assertTrue(activity.mWmService.mLetterboxConfiguration
+ .isCompatFakeFocusEnabled(activity.info));
+ }
+
+ @Test
+ public void testIsCompatFakeFocusEnabled_optOutPropertyEnabled_fakeFocusDisabled() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT));
+
+ assertFalse(activity.mWmService.mLetterboxConfiguration
+ .isCompatFakeFocusEnabled(activity.info));
+ }
+
+ @Test
+ public void testIsCompatFakeFocusEnabled_optInPropertyEnabled_fakeFocusEnabled() {
+ ActivityRecord activity = setUpActivityForCompatFakeFocusTest();
+ doReturn(true).when(activity.mWmService.mLetterboxConfiguration)
+ .getPackageManagerProperty(any(), eq(PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN));
+
+ assertTrue(activity.mWmService.mLetterboxConfiguration
+ .isCompatFakeFocusEnabled(activity.info));
+ }
+
private int getExpectedSplitSize(int dimensionToSplit) {
int dividerWindowWidth =
mActivity.mWmService.mContext.getResources().getDimensionPixelSize(