Merge "Bluetooth : Correct the Network reset handling" into r-keystone-qcom-dev
diff --git a/Android.bp b/Android.bp
index 632f49d..7c2d6eb 100755
--- a/Android.bp
+++ b/Android.bp
@@ -264,6 +264,7 @@
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
":libupdate_engine_aidl",
+ ":resourcemanager_aidl",
":storaged_aidl",
":vold_aidl",
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9dcba7c..5987a72 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -951,6 +951,7 @@
const int64_t& nextBucketStartTimeNs) {
if (mCondition == ConditionState::kUnknown) {
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
+ invalidateCurrentBucketWithoutResetBase(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
}
VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs,
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 13d977f..1e6680c 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -13,6 +13,11 @@
// limitations under the License.
#include "StatsLogProcessor.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
#include "StatsService.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -20,16 +25,10 @@
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
-#include "storage/StorageManager.h"
#include "statslog_statsdtest.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
+#include "storage/StorageManager.h"
#include "tests/statsd_test_util.h"
-#include <stdio.h>
-
using namespace android;
using namespace testing;
using ::ndk::SharedRefBase;
@@ -1836,6 +1835,11 @@
int isolatedUid = 30;
sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
ConfigKey key(3, 4);
+
+ // TODO: All tests should not persist state on disk. This removes any reports that were present.
+ ProtoOutputStream proto;
+ StorageManager::appendConfigMetricsReport(key, &proto, /*erase data=*/true, /*isAdb=*/false);
+
StatsdConfig config = MakeConfig(false);
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index f041996..5666501 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -3295,11 +3295,15 @@
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
- ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+ ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(1);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis());
}
/*
@@ -3615,7 +3619,7 @@
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerWithCondition(
- pullerManager, metric, ConditionState::kUnknown);
+ pullerManager, metric, ConditionState::kFalse);
// Check dump report.
ProtoOutputStream output;
@@ -3641,6 +3645,94 @@
}
/*
+ * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kUnknown);
+
+ // Bucket should be dropped because of condition unknown.
+ int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC;
+ valueProducer->notifyAppUpgrade(appUpgradeTimeNs);
+
+ // Bucket also dropped due to condition unknown
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // This bucket is also dropped due to condition unknown.
+ int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC;
+ valueProducer->onConditionChanged(true, conditionChangeTimeNs);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(3, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
+
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+ report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(1).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
+
+ dropEvent = report.value_metrics().skipped(1).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ report.value_metrics().skipped(2).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+ report.value_metrics().skipped(2).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size());
+
+ dropEvent = report.value_metrics().skipped(2).drop_event(0);
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
* Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
* was not flushed in time.
*/
@@ -4957,7 +5049,7 @@
ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
- EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
+ EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ddc5747..e620f16 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -261,8 +261,9 @@
< SystemClock.elapsedRealtime()) {
String stackTrace = getFormattedStackTrace();
try {
+ String packageName = ActivityThread.currentOpPackageName();
sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
- ActivityThread.currentOpPackageName(), op, stackTrace);
+ packageName == null ? "" : packageName, op, stackTrace);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c8dd4d9..885ffba 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -205,6 +205,7 @@
public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
+ public static final String TAG_PROFILEABLE = "profileable";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -459,6 +460,9 @@
public final SigningDetails signingDetails;
public final boolean coreApp;
public final boolean debuggable;
+ // This does not represent the actual manifest structure since the 'profilable' tag
+ // could be used with attributes other than 'shell'. Extend if necessary.
+ public final boolean profilableByShell;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
@@ -470,15 +474,13 @@
public final int overlayPriority;
public ApkLite(String codePath, String packageName, String splitName,
- boolean isFeatureSplit,
- String configForSplit, String usesSplitName, boolean isSplitRequired,
- int versionCode, int versionCodeMajor,
- int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- SigningDetails signingDetails, boolean coreApp,
- boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
- String targetPackageName, boolean overlayIsStatic, int overlayPriority,
- int minSdkVersion, int targetSdkVersion) {
+ boolean isFeatureSplit, String configForSplit, String usesSplitName,
+ boolean isSplitRequired, int versionCode, int versionCodeMajor, int revisionCode,
+ int installLocation, List<VerifierInfo> verifiers, SigningDetails signingDetails,
+ boolean coreApp, boolean debuggable, boolean profilableByShell, boolean multiArch,
+ boolean use32bitAbi, boolean useEmbeddedDex, boolean extractNativeLibs,
+ boolean isolatedSplits, String targetPackageName, boolean overlayIsStatic,
+ int overlayPriority, int minSdkVersion, int targetSdkVersion) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -493,6 +495,7 @@
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
this.coreApp = coreApp;
this.debuggable = debuggable;
+ this.profilableByShell = profilableByShell;
this.multiArch = multiArch;
this.use32bitAbi = use32bitAbi;
this.useEmbeddedDex = useEmbeddedDex;
@@ -1573,6 +1576,7 @@
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
+ boolean profilableByShell = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
@@ -1638,6 +1642,10 @@
final String attr = attrs.getAttributeName(i);
if ("debuggable".equals(attr)) {
debuggable = attrs.getAttributeBooleanValue(i, false);
+ if (debuggable) {
+ // Debuggable implies profileable
+ profilableByShell = true;
+ }
}
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -1690,6 +1698,13 @@
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
+ } else if (TAG_PROFILEABLE.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("shell".equals(attr)) {
+ profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+ }
+ }
}
}
@@ -1707,8 +1722,9 @@
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
- multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
- targetPackage, overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion);
+ profilableByShell, multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs,
+ isolatedSplits, targetPackage, overlayIsStatic, overlayPriority, minSdkVersion,
+ targetSdkVersion);
}
/**
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index d2172d3..c3e9402 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -20,7 +20,6 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -303,6 +302,7 @@
int revisionCode = 0;
boolean coreApp = false;
boolean debuggable = false;
+ boolean profilableByShell = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
@@ -379,6 +379,10 @@
switch (attr) {
case "debuggable":
debuggable = attrs.getAttributeBooleanValue(i, false);
+ if (debuggable) {
+ // Debuggable implies profileable
+ profilableByShell = true;
+ }
break;
case "multiArch":
multiArch = attrs.getAttributeBooleanValue(i, false);
@@ -431,6 +435,13 @@
minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
}
}
+ } else if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("shell".equals(attr)) {
+ profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
+ }
+ }
}
}
@@ -445,12 +456,13 @@
overlayPriority = 0;
}
- return input.success(new PackageParser.ApkLite(codePath, packageSplit.first,
- packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, isSplitRequired,
- versionCode, versionCodeMajor, revisionCode, installLocation, verifiers,
- signingDetails, coreApp, debuggable, multiArch, use32bitAbi, useEmbeddedDex,
- extractNativeLibs, isolatedSplits, targetPackage, overlayIsStatic, overlayPriority,
- minSdkVersion, targetSdkVersion));
+ return input.success(
+ new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
+ isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+ versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
+ coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
+ useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
+ overlayIsStatic, overlayPriority, minSdkVersion, targetSdkVersion));
}
public static ParseResult<Pair<String, String>> parsePackageSplitNames(ParseInput input,
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 220ce22..61e6a05 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -111,6 +111,11 @@
void deleteStorage(int storageId);
/**
+ * Permanently disable readlogs reporting for a storage given its ID.
+ */
+ void disableReadLogs(int storageId);
+
+ /**
* Setting up native library directories and extract native libs onto a storage if needed.
*/
boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi, boolean extractNativeLibs);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 863d86ef..31ccf95 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -153,6 +153,13 @@
}
/**
+ * Permanently disables readlogs.
+ */
+ public void disableReadLogs() {
+ mDefaultStorage.disableReadLogs();
+ }
+
+ /**
* Resets the states and unbinds storage instances for an installation session.
* TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
*/
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 6200a38..ca6114f 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -418,6 +418,17 @@
private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
/**
+ * Permanently disable readlogs collection.
+ */
+ public void disableReadLogs() {
+ try {
+ mService.disableReadLogs(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Deserialize and validate v4 signature bytes.
*/
private static void validateV4Signature(@Nullable byte[] v4signatureBytes)
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index ef21900..4adcd69 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -219,6 +219,9 @@
/**
* Listen for changes to observed cell info.
*
+ * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission.
+ *
* @see #onCellInfoChanged
*/
public static final int LISTEN_CELL_INFO = 0x00000400;
@@ -461,6 +464,9 @@
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onRegistrationFailed
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -472,6 +478,9 @@
* <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
* the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
*
+ * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless
+ * of whether the calling app has carrier privileges.
+ *
* @see #onBarringInfoChanged
*/
@RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -569,6 +578,11 @@
* subId. Otherwise, this callback applies to
* {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
+ * The instance of {@link ServiceState} passed as an argument here will have various levels of
+ * location information stripped from it depending on the location permissions that your app
+ * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will
+ * receive all the information in {@link ServiceState}.
+ *
* @see ServiceState#STATE_EMERGENCY_ONLY
* @see ServiceState#STATE_IN_SERVICE
* @see ServiceState#STATE_OUT_OF_SERVICE
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 397b04e..17620fa 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -357,6 +357,19 @@
return mSources.get(type);
}
+ /**
+ * Returns the source visibility or the default visibility if the source doesn't exist. This is
+ * useful if when treating this object as a request.
+ *
+ * @param type The {@link InternalInsetsType} to query.
+ * @return {@code true} if the source is visible or the type is default visible and the source
+ * doesn't exist.
+ */
+ public boolean getSourceOrDefaultVisibility(@InternalInsetsType int type) {
+ final InsetsSource source = mSources.get(type);
+ return source != null ? source.isVisible() : getDefaultVisibility(type);
+ }
+
public void setDisplayFrame(Rect frame) {
mDisplayFrame.set(frame);
}
@@ -388,20 +401,6 @@
}
}
- /**
- * A shortcut for setting the visibility of the source.
- *
- * @param type The {@link InternalInsetsType} of the source to set the visibility
- * @param referenceState The {@link InsetsState} for reference
- */
- public void setSourceVisible(@InternalInsetsType int type, InsetsState referenceState) {
- InsetsSource source = mSources.get(type);
- InsetsSource referenceSource = referenceState.mSources.get(type);
- if (source != null && referenceSource != null) {
- source.setVisible(referenceSource.isVisible());
- }
- }
-
public void set(InsetsState other) {
set(other, false /* copySources */);
}
@@ -490,7 +489,7 @@
}
}
- public static boolean getDefaultVisibility(@InsetsType int type) {
+ public static boolean getDefaultVisibility(@InternalInsetsType int type) {
return type != ITYPE_IME;
}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index d63ebda..fc8cafd 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -427,6 +427,8 @@
&& dri.getResolveInfo().targetUserId == UserHandle.USER_CURRENT) {
if (shouldAddResolveInfo(dri)) {
mDisplayList.add(dri);
+ Log.i(TAG, "Add DisplayResolveInfo component: " + dri.getResolvedComponentName()
+ + ", intent component: " + dri.getResolvedIntent().getComponent());
}
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 21ca948..d9ca9c2 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1197,6 +1197,10 @@
addFeature(PackageManager.FEATURE_APP_ENUMERATION, 0);
}
+ if (Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.Q) {
+ addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
+ }
+
for (String featureName : mUnavailableFeatures) {
removeFeature(featureName);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 80577ce..e692b04 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3571,6 +3571,8 @@
@hide -->
<permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
+
<!-- Must be required by a {@link android.media.routing.MediaRouteService}
to ensure that only the system can interact with it.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0f30544..40d47a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4365,9 +4365,6 @@
<!-- The delete-widget drop target button text -->
<string name="kg_reordering_delete_drop_target_text">Remove</string>
- <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
- <string name="allow_while_in_use_permission_in_fgs">The background started foreground service from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will not have while-in-use permission in future R builds. Please see go/r-bg-fgs-restriction and file a bugreport.</string>
-
<!-- Message shown in dialog when user is attempting to set the music volume above the
recommended maximum level for headphones -->
<string name="safe_media_volume_warning" product="default">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ae3b82e..4b3d5168 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4034,9 +4034,6 @@
<java-symbol type="dimen" name="resolver_empty_state_container_padding_top" />
<java-symbol type="dimen" name="resolver_empty_state_container_padding_bottom" />
- <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
- <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
-
<java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" />
<!-- Whether to expand the lock screen user switcher by default -->
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 308c1a5..4f4364f 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -13,26 +13,26 @@
// limitations under the License.
android_library_import {
- name: "window-extensions",
- aars: ["window-extensions-release.aar"],
+ name: "window-sidecar",
+ aars: ["window-sidecar-release.aar"],
sdk_version: "current",
}
java_library {
- name: "androidx.window.extensions",
+ name: "androidx.window.sidecar",
srcs: ["src/**/*.java"],
- static_libs: ["window-extensions"],
+ static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
vendor: true,
libs: ["framework", "androidx.annotation_annotation",],
- required: ["androidx.window.extensions.xml",],
+ required: ["androidx.window.sidecar.xml",],
}
prebuilt_etc {
- name: "androidx.window.extensions.xml",
+ name: "androidx.window.sidecar.xml",
vendor: true,
sub_dir: "permissions",
- src: "androidx.window.extensions.xml",
+ src: "androidx.window.sidecar.xml",
filename_from_src: true,
}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
deleted file mode 100644
index 1f0ff66..0000000
--- a/libs/WindowManager/Jetpack/androidx.window.extensions.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2020 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.
- -->
-<permissions>
- <library
- name="androidx.window.extensions"
- file="/vendor/framework/androidx.window.extensions.jar"/>
-</permissions>
diff --git a/libs/WindowManager/Jetpack/androidx.window.sidecar.xml b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
new file mode 100644
index 0000000..f88a5f4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.sidecar.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+<permissions>
+ <library
+ name="androidx.window.sidecar"
+ file="/vendor/framework/androidx.window.sidecar.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
similarity index 79%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
index 7a3fbf3..92e5758 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.window.extensions.ExtensionHelper.getWindowDisplay;
-import static androidx.window.extensions.ExtensionHelper.isInMultiWindow;
-import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation;
-import static androidx.window.extensions.ExtensionHelper.transformToWindowSpaceRect;
+import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
+import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
+import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
+import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
import android.content.ContentResolver;
import android.content.Context;
@@ -42,8 +42,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-class SettingsExtensionImpl extends StubExtension {
- private static final String TAG = "SettingsExtension";
+class SettingsSidecarImpl extends StubSidecar {
+ private static final String TAG = "SettingsSidecar";
private static final String DEVICE_POSTURE = "device_posture";
private static final String DISPLAY_FEATURES = "display_features";
@@ -106,7 +106,7 @@
}
}
- SettingsExtensionImpl(Context context) {
+ SettingsSidecarImpl(Context context) {
mContext = context;
mSettingsObserver = new SettingsObserver();
}
@@ -118,29 +118,33 @@
/** Update display features with values read from settings. */
private void updateDisplayFeatures() {
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
- ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+ SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
updateWindowLayout(windowToken, newLayout);
}
}
@NonNull
@Override
- public ExtensionDeviceState getDeviceState() {
+ public SidecarDeviceState getDeviceState() {
ContentResolver resolver = mContext.getContentResolver();
int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
- ExtensionDeviceState.POSTURE_UNKNOWN);
- return new ExtensionDeviceState(posture);
+ SidecarDeviceState.POSTURE_UNKNOWN);
+ SidecarDeviceState deviceState = new SidecarDeviceState();
+ deviceState.posture = posture;
+ return deviceState;
}
@NonNull
@Override
- public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
- List<ExtensionDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
- return new ExtensionWindowLayoutInfo(displayFeatures);
+ public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+ List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
+ SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+ windowLayoutInfo.displayFeatures = displayFeatures;
+ return windowLayoutInfo;
}
- private List<ExtensionDisplayFeature> readDisplayFeatures(IBinder windowToken) {
- List<ExtensionDisplayFeature> features = new ArrayList<ExtensionDisplayFeature>();
+ private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
+ List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
int displayId = getWindowDisplay(windowToken);
if (displayId != DEFAULT_DISPLAY) {
Log.w(TAG, "This sample doesn't support display features on secondary displays");
@@ -170,10 +174,10 @@
int type;
switch (featureType) {
case FEATURE_TYPE_FOLD:
- type = ExtensionDisplayFeature.TYPE_FOLD;
+ type = SidecarDisplayFeature.TYPE_FOLD;
break;
case FEATURE_TYPE_HINGE:
- type = ExtensionDisplayFeature.TYPE_HINGE;
+ type = SidecarDisplayFeature.TYPE_HINGE;
break;
default: {
Log.e(TAG, "Malformed feature type: " + featureType);
@@ -189,8 +193,9 @@
rotateRectToDisplayRotation(featureRect, displayId);
transformToWindowSpaceRect(featureRect, windowToken);
if (!featureRect.isEmpty()) {
- ExtensionDisplayFeature feature =
- new ExtensionDisplayFeature(featureRect, type);
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ feature.setRect(featureRect);
+ feature.setType(type);
features.add(feature);
} else {
Log.w(TAG, "Failed to adjust feature to window");
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
similarity index 92%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
index c61f1ed..e5b6cff 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
@@ -32,12 +32,7 @@
import androidx.annotation.Nullable;
-/**
- * Toolkit class for calculation of the display feature bounds within the window.
- * NOTE: This sample implementation only works for Activity windows, because there is no public APIs
- * to obtain layout params or bounds for arbitrary windows.
- */
-class ExtensionHelper {
+class SidecarHelper {
/**
* Rotate the input rectangle specified in default display orientation to the current display
* rotation.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
similarity index 70%
rename from libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
rename to libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 47349f1..0b4915e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.window.extensions;
+package androidx.window.sidecar;
import android.content.Context;
@@ -22,14 +22,13 @@
* Provider class that will instantiate the library implementation. It must be included in the
* vendor library, and the vendor implementation must match the signature of this class.
*/
-public class ExtensionProvider {
-
+public class SidecarProvider {
/**
- * The support library will instantiate the vendor implementation using this interface.
- * @return An implementation of {@link ExtensionInterface}.
+ * Provide a simple implementation of {@link SidecarInterface} that can be replaced by
+ * an OEM by overriding this method.
*/
- public static ExtensionInterface getExtensionImpl(Context context) {
- return new SettingsExtensionImpl(context);
+ public static SidecarInterface getSidecarImpl(Context context) {
+ return new SettingsSidecarImpl(context);
}
/**
@@ -37,6 +36,6 @@
* @return API version string in MAJOR.MINOR.PATCH-description format.
*/
public static String getApiVersion() {
- return "1.0.0-settings_sample";
+ return "0.1.0-settings_sample";
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
new file mode 100644
index 0000000..199c373
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 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 androidx.window.sidecar;
+
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubSidecar implements SidecarInterface {
+
+ private SidecarCallback mSidecarCallback;
+ private final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
+ private boolean mDeviceStateChangeListenerRegistered;
+
+ StubSidecar() {
+ }
+
+ @Override
+ public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) {
+ this.mSidecarCallback = sidecarCallback;
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) {
+ this.mWindowLayoutChangeListenerTokens.add(iBinder);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) {
+ this.mWindowLayoutChangeListenerTokens.remove(iBinder);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onDeviceStateListenersChanged(boolean isEmpty) {
+ this.mDeviceStateChangeListenerRegistered = !isEmpty;
+ this.onListenersChanged();
+ }
+
+ void updateDeviceState(SidecarDeviceState newState) {
+ if (this.mSidecarCallback != null) {
+ mSidecarCallback.onDeviceStateChanged(newState);
+ }
+ }
+
+ void updateWindowLayout(@NonNull IBinder windowToken,
+ @NonNull SidecarWindowLayoutInfo newLayout) {
+ if (this.mSidecarCallback != null) {
+ mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout);
+ }
+ }
+
+ @NonNull
+ Set<IBinder> getWindowsListeningForLayoutChanges() {
+ return mWindowLayoutChangeListenerTokens;
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered;
+ }
+
+ protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
deleted file mode 100644
index 0ebbb86..0000000
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ /dev/null
Binary files differ
diff --git a/libs/WindowManager/Jetpack/window-sidecar-release.aar b/libs/WindowManager/Jetpack/window-sidecar-release.aar
new file mode 100644
index 0000000..50f101d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-sidecar-release.aar
Binary files differ
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index a18cfccb..5a7c87e0 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -576,6 +576,10 @@
}
void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
+ if (preferredFeatures == null) {
+ mPreferredFeaturesMap.remove(packageName);
+ return;
+ }
List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
if ((prevFeatures == null && preferredFeatures.size() == 0)
|| Objects.equals(preferredFeatures, prevFeatures)) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 4c720ab..37dfce4 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -16,10 +16,10 @@
package com.android.systemui.car.navigationbar;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -368,15 +368,13 @@
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("TopCarNavigationBar");
- lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
- lp.setFitInsetsTypes(0);
lp.windowAnimations = 0;
lp.gravity = Gravity.TOP;
mWindowManager.addView(mTopNavigationBarWindow, lp);
@@ -390,14 +388,13 @@
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("BottomCarNavigationBar");
- lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR, ITYPE_BOTTOM_GESTURES};
lp.windowAnimations = 0;
lp.gravity = Gravity.BOTTOM;
mWindowManager.addView(mBottomNavigationBarWindow, lp);
@@ -415,6 +412,8 @@
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
leftlp.setTitle("LeftCarNavigationBar");
+ leftlp.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
+ leftlp.setFitInsetsTypes(0);
leftlp.windowAnimations = 0;
leftlp.gravity = Gravity.LEFT;
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
@@ -432,6 +431,8 @@
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
rightlp.setTitle("RightCarNavigationBar");
+ rightlp.providesInsetsTypes = new int[]{ITYPE_EXTRA_NAVIGATION_BAR};
+ rightlp.setFitInsetsTypes(0);
rightlp.windowAnimations = 0;
rightlp.gravity = Gravity.RIGHT;
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 0ced402..029d4c7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,10 +16,7 @@
package com.android.systemui.car.navigationbar;
-import static android.view.WindowInsets.Type.systemBars;
-
import android.content.Context;
-import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -82,28 +79,9 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
- applyMargins(windowInsets.getInsets(systemBars()));
return windowInsets;
}
- private void applyMargins(Insets insets) {
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getLayoutParams() instanceof LayoutParams) {
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
- || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
- lp.rightMargin = insets.right;
- lp.leftMargin = insets.left;
- lp.topMargin = insets.top;
- lp.bottomMargin = insets.bottom;
- child.requestLayout();
- }
- }
- }
- }
-
// Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 3bf43dd..3565b0e 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -30,9 +30,7 @@
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.DrawableWrapper;
import android.os.RemoteException;
-import android.util.DisplayMetrics;
import android.util.PathParser;
-import android.view.Display;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -47,6 +45,9 @@
*/
public class AdaptiveOutlineDrawable extends DrawableWrapper {
+ private static final float ADVANCED_ICON_CENTER = 50f;
+ private static final float ADVANCED_ICON_RADIUS = 48f;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_DEFAULT, TYPE_ADVANCED})
public @interface AdaptiveOutlineIconType {
@@ -61,7 +62,6 @@
private int mStrokeWidth;
private Bitmap mBitmap;
private int mType;
- private float mDensity;
public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
super(new AdaptiveIconShapeDrawable(resources));
@@ -83,7 +83,6 @@
mPath = new Path(PathParser.createPathFromPathData(
resources.getString(com.android.internal.R.string.config_icon_mask)));
mStrokeWidth = resources.getDimensionPixelSize(R.dimen.adaptive_outline_stroke);
- mDensity = resources.getDisplayMetrics().density;
mOutlinePaint = new Paint();
mOutlinePaint.setColor(getColor(resources, type));
mOutlinePaint.setStyle(Paint.Style.STROKE);
@@ -137,12 +136,7 @@
if (mType == TYPE_DEFAULT) {
canvas.drawPath(mPath, mOutlinePaint);
} else {
- final float defaultDensity = getDefaultDisplayDensity(Display.DEFAULT_DISPLAY)
- / (float) DisplayMetrics.DENSITY_DEFAULT;
- final int insetPx =
- Math.round(mInsetPx / (float) (Math.floor(
- (mDensity / defaultDensity) * 100) / 100.0));
- canvas.drawCircle(2 * insetPx, 2 * insetPx, 2 * insetPx - mStrokeWidth,
+ canvas.drawCircle(ADVANCED_ICON_CENTER, ADVANCED_ICON_CENTER, ADVANCED_ICON_RADIUS,
mOutlinePaint);
}
canvas.restoreToCount(count);
diff --git a/packages/SettingsLib/SearchWidget/res/values-el/strings.xml b/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
index 6f5ab78..d50436a 100644
--- a/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-el/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Ρυθμίσεις αναζήτησης"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Αναζήτηση στις ρυθμίσεις"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml b/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
index 8fa5a84..b68b792 100644
--- a/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-hy/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Որոնման կարգավորումներ"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Որոնեք կարգավորումներ"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
index 85a8d73..5fe116e 100644
--- a/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-pt-rPT/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Pesquisa de definições"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Pesquisar nas definições"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml b/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
index a531321..354941d 100644
--- a/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-sq/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Cilësimet e kërkimit"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Kërko te cilësimet"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml b/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
index dfd66b2..560ac13 100644
--- a/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-uk/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Налаштування пошуку"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Пошук налаштувань"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml b/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
index cb1a75a..90daf11 100644
--- a/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-vi/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="search_menu" msgid="1914043873178389845">"Tìm kiếm trong các mục cài đặt"</string>
+ <string name="search_menu" msgid="1914043873178389845">"Tìm trong thông tin cài đặt"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 137116f..376c2e7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -239,7 +239,7 @@
<string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Pantaila ez da ezarriko inoiz inaktibo kargatu bitartean"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"Gaitu Bluetooth HCI miatze-erregistroa"</string>
- <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetooth konexioa ezarpena aldatu ostean)."</string>
+ <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Hauteman Bluetooth paketeak (aktibatu edo desaktibatu Bluetooth-a ezarpena aldatu ostean)."</string>
<string name="oem_unlock_enable" msgid="5334869171871566731">"OEM desblokeoa"</string>
<string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Onartu abiarazlea desblokeatzea"</string>
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"OEM desblokeoa onartu nahi duzu?"</string>
diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml
index de38625..3018683 100644
--- a/packages/SettingsLib/res/values-it/arrays.xml
+++ b/packages/SettingsLib/res/values-it/arrays.xml
@@ -22,13 +22,13 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string-array name="wifi_status">
<item msgid="1596683495752107015"></item>
- <item msgid="3288373008277313483">"Scansione in corso..."</item>
- <item msgid="6050951078202663628">"Connessione..."</item>
- <item msgid="8356618438494652335">"Autenticazione..."</item>
- <item msgid="2837871868181677206">"Acquisizione indirizzo IP..."</item>
+ <item msgid="3288373008277313483">"Scansione in corso…"</item>
+ <item msgid="6050951078202663628">"Connessione…"</item>
+ <item msgid="8356618438494652335">"Autenticazione…"</item>
+ <item msgid="2837871868181677206">"Acquisizione indirizzo IP…"</item>
<item msgid="4613015005934755724">"Connessa"</item>
<item msgid="3763530049995655072">"Sospesa"</item>
- <item msgid="7852381437933824454">"Disconnessione..."</item>
+ <item msgid="7852381437933824454">"Disconnessione…"</item>
<item msgid="5046795712175415059">"Disconnessa"</item>
<item msgid="2473654476624070462">"Operazione non riuscita"</item>
<item msgid="9146847076036105115">"Bloccato"</item>
@@ -36,13 +36,13 @@
</string-array>
<string-array name="wifi_status_with_ssid">
<item msgid="5969842512724979061"></item>
- <item msgid="1818677602615822316">"Scansione in corso..."</item>
- <item msgid="8339720953594087771">"Connessione a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
- <item msgid="3028983857109369308">"Autenticazione con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
- <item msgid="4287401332778341890">"Acquisizione indirizzo IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="1818677602615822316">"Scansione in corso…"</item>
+ <item msgid="8339720953594087771">"Connessione a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="3028983857109369308">"Autenticazione con <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="4287401332778341890">"Acquisizione indirizzo IP da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="1043944043827424501">"Connessa a <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
<item msgid="7445993821842009653">"Sospesa"</item>
- <item msgid="1175040558087735707">"Disconnessione da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="1175040558087735707">"Disconnessione da <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
<item msgid="699832486578171722">"Disconnessa"</item>
<item msgid="522383512264986901">"Operazione non riuscita"</item>
<item msgid="3602596701217484364">"Bloccato"</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index 38eeda2..8987968 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -22,10 +22,12 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
import android.net.Uri;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -146,10 +148,23 @@
/**
* Returns a boolean indicating whether a given package is a mainline module.
*/
- public static boolean isMainlineModule(Context context, String packageName) {
- final PackageManager pm = context.getPackageManager();
+ public static boolean isMainlineModule(PackageManager pm, String packageName) {
+ // Check if the package is listed among the system modules.
try {
- return pm.getModuleInfo(packageName, 0 /* flags */) != null;
+ pm.getModuleInfo(packageName, 0 /* flags */);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ //pass
+ }
+
+ try {
+ final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
+ // Check if the package is contained in an APEX. There is no public API to properly
+ // check whether a given APK package comes from an APEX registered as module.
+ // Therefore we conservatively assume that any package scanned from an /apex path is
+ // a system package.
+ return pkg.applicationInfo.sourceDir.startsWith(
+ Environment.getApexDirectory().getAbsolutePath());
} catch (PackageManager.NameNotFoundException e) {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
old mode 100644
new mode 100755
index d48aa24..a0a91a1
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -242,7 +242,17 @@
final TimeZoneNames.NameType nameType =
tz.inDaylightTime(now) ? TimeZoneNames.NameType.LONG_DAYLIGHT
: TimeZoneNames.NameType.LONG_STANDARD;
- return names.getDisplayName(tz.getID(), nameType, now.getTime());
+ // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs
+ // that match ICUs zone IDs (which are similar but not guaranteed the same as those
+ // in timezones.xml). timezones.xml and related files uses the IANA IDs. ICU IDs are
+ // stable and IANA IDs have changed over time so they have drifted.
+ // See http://bugs.icu-project.org/trac/ticket/13070 / http://b/36469833.
+ String canonicalZoneId = android.icu.util.TimeZone.getCanonicalID(tz.getID());
+ if (canonicalZoneId == null) {
+ canonicalZoneId = tz.getID();
+ }
+
+ return names.getDisplayName(canonicalZoneId, nameType, now.getTime());
}
private static void appendWithTtsSpan(SpannableStringBuilder builder, CharSequence content,
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1faa16a..5f47cfe 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -243,6 +243,9 @@
<!-- Default for Settings.Secure.AWARE_LOCK_ENABLED -->
<bool name="def_aware_lock_enabled">false</bool>
+ <!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED -->
+ <bool name="def_hdmiControlAutoDeviceOff">false</bool>
+
<!-- Default for Settings.System.TIME_12_24 -->
<string name="def_time_format" translatable="false">12</string>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 95c3e03..5322609 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3513,7 +3513,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 190;
+ private static final int SETTINGS_VERSION = 191;
private final int mUserId;
@@ -4877,6 +4877,21 @@
currentVersion = 190;
}
+ if (currentVersion == 190) {
+ // Version 190: get HDMI auto device off from overlay
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ final Setting currentSetting = globalSettings.getSettingLocked(
+ Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
+ if (currentSetting.isNull()) {
+ globalSettings.insertSettingLocked(
+ Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+ getContext().getResources().getBoolean(
+ R.bool.def_hdmiControlAutoDeviceOff) ? "1" : "0",
+ null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 191;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 1a4dd1e..1db2e32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -25,12 +25,16 @@
import static java.lang.Integer.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -48,9 +52,13 @@
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -64,6 +72,7 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -100,6 +109,8 @@
private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
+ private static final long IME_DISAPPEAR_DURATION_MS = 125;
+
private KeyguardSecurityModel mSecurityModel;
private LockPatternUtils mLockPatternUtils;
@@ -125,6 +136,7 @@
private int mActivePointerId = -1;
private boolean mIsDragging;
private float mStartTouchY = -1;
+ private boolean mDisappearAnimRunning;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -148,22 +160,29 @@
public WindowInsets onProgress(WindowInsets windowInsets,
List<WindowInsetsAnimation> list) {
int translationY = 0;
- for (WindowInsetsAnimation animation : list) {
- if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
- continue;
+ if (mDisappearAnimRunning) {
+ mSecurityViewFlipper.setTranslationY(
+ mInitialBounds.bottom - mFinalBounds.bottom);
+ } else {
+ for (WindowInsetsAnimation animation : list) {
+ if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
+ continue;
+ }
+ final int paddingBottom = (int) MathUtils.lerp(
+ mInitialBounds.bottom - mFinalBounds.bottom, 0,
+ animation.getInterpolatedFraction());
+ translationY += paddingBottom;
}
- final int paddingBottom = (int) MathUtils.lerp(
- mInitialBounds.bottom - mFinalBounds.bottom, 0,
- animation.getInterpolatedFraction());
- translationY += paddingBottom;
+ mSecurityViewFlipper.setTranslationY(translationY);
}
- mSecurityViewFlipper.setTranslationY(translationY);
return windowInsets;
}
@Override
public void onEnd(WindowInsetsAnimation animation) {
- mSecurityViewFlipper.setTranslationY(0);
+ if (!mDisappearAnimRunning) {
+ mSecurityViewFlipper.setTranslationY(0);
+ }
}
};
@@ -376,8 +395,51 @@
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
+ mDisappearAnimRunning = true;
if (mCurrentSecuritySelection == SecurityMode.Password) {
- mSecurityViewFlipper.getWindowInsetsController().hide(WindowInsets.Type.ime());
+ mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
+ IME_DISAPPEAR_DURATION_MS,
+ Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
+
+
+ @Override
+ public void onReady(@NonNull WindowInsetsAnimationController controller,
+ int types) {
+ ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f);
+ anim.addUpdateListener(animation -> {
+ if (controller.isCancelled()) {
+ return;
+ }
+ Insets shownInsets = controller.getShownStateInsets();
+ Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0,
+ (int) (-shownInsets.bottom / 4
+ * anim.getAnimatedFraction())));
+ controller.setInsetsAndAlpha(insets,
+ (float) animation.getAnimatedValue(),
+ anim.getAnimatedFraction());
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ controller.finish(false);
+ }
+ });
+ anim.setDuration(IME_DISAPPEAR_DURATION_MS);
+ anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ anim.start();
+ }
+
+ @Override
+ public void onFinished(
+ @NonNull WindowInsetsAnimationController controller) {
+ mDisappearAnimRunning = false;
+ }
+
+ @Override
+ public void onCancelled(
+ @Nullable WindowInsetsAnimationController controller) {
+ }
+ });
}
if (mCurrentSecuritySelection != SecurityMode.None) {
return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
@@ -887,6 +949,7 @@
@Override
public void reset() {
mSecurityViewFlipper.reset();
+ mDisappearAnimRunning = false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 6da7bc8..7f78ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.bubbles;
+import static android.app.Notification.FLAG_BUBBLE;
import static android.os.AsyncTask.Status.FINISHED;
import static android.view.Display.INVALID_DISPLAY;
@@ -28,19 +29,21 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Path;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.InstanceId;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -54,12 +57,17 @@
class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
+ /**
+ * NotificationEntry associated with the bubble. A null value implies this bubble is loaded
+ * from disk.
+ */
+ @Nullable
+ private NotificationEntry mEntry;
private final String mKey;
private long mLastUpdated;
private long mLastAccessed;
- @Nullable
private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
/** Whether the bubble should show a dot for the notification indicating updated content. */
@@ -67,6 +75,8 @@
/** Whether flyout text should be suppressed, regardless of any other flags or state. */
private boolean mSuppressFlyout;
+ /** Whether this bubble should auto expand regardless of the normal flag, used for overflow. */
+ private boolean mShouldAutoExpand;
// Items that are typically loaded later
private String mAppName;
@@ -82,7 +92,6 @@
* Presentational info about the flyout.
*/
public static class FlyoutMessage {
- @Nullable public Icon senderIcon;
@Nullable public Drawable senderAvatar;
@Nullable public CharSequence senderName;
@Nullable public CharSequence message;
@@ -100,39 +109,16 @@
private UserHandle mUser;
@NonNull
private String mPackageName;
- @Nullable
- private String mTitle;
- @Nullable
- private Icon mIcon;
- private boolean mIsBubble;
- private boolean mIsVisuallyInterruptive;
- private boolean mIsClearable;
- private boolean mShouldSuppressNotificationDot;
- private boolean mShouldSuppressNotificationList;
- private boolean mShouldSuppressPeek;
private int mDesiredHeight;
@DimenRes
private int mDesiredHeightResId;
- /** for logging **/
- @Nullable
- private InstanceId mInstanceId;
- @Nullable
- private String mChannelId;
- private int mNotificationId;
- private int mAppUid = -1;
-
- @Nullable
- private PendingIntent mIntent;
- @Nullable
- private PendingIntent mDeleteIntent;
-
/**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
- final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
+ final int desiredHeight, final int desiredHeightResId) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mShortcutInfo = shortcutInfo;
@@ -140,10 +126,8 @@
mFlags = 0;
mUser = shortcutInfo.getUserHandle();
mPackageName = shortcutInfo.getPackage();
- mIcon = shortcutInfo.getIcon();
mDesiredHeight = desiredHeight;
mDesiredHeightResId = desiredHeightResId;
- mTitle = title;
}
/** Used in tests when no UI is required. */
@@ -161,6 +145,12 @@
return mKey;
}
+ @Nullable
+ public NotificationEntry getEntry() {
+ return mEntry;
+ }
+
+ @NonNull
public UserHandle getUser() {
return mUser;
}
@@ -213,7 +203,14 @@
@Nullable
public String getTitle() {
- return mTitle;
+ final CharSequence titleCharSeq;
+ if (mEntry == null) {
+ titleCharSeq = null;
+ } else {
+ titleCharSeq = mEntry.getSbn().getNotification().extras.getCharSequence(
+ Notification.EXTRA_TITLE);
+ }
+ return titleCharSeq != null ? titleCharSeq.toString() : null;
}
/**
@@ -334,44 +331,17 @@
void setEntry(@NonNull final NotificationEntry entry) {
Objects.requireNonNull(entry);
Objects.requireNonNull(entry.getSbn());
+ mEntry = entry;
mLastUpdated = entry.getSbn().getPostTime();
- mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
+ mFlags = entry.getSbn().getNotification().flags;
mPackageName = entry.getSbn().getPackageName();
mUser = entry.getSbn().getUser();
- mTitle = getTitle(entry);
- mIsClearable = entry.isClearable();
- mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
- mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
- mShouldSuppressPeek = entry.shouldSuppressPeek();
- mChannelId = entry.getSbn().getNotification().getChannelId();
- mNotificationId = entry.getSbn().getId();
- mAppUid = entry.getSbn().getUid();
- mInstanceId = entry.getSbn().getInstanceId();
- mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
- if (entry.getRanking() != null) {
- mShortcutInfo = entry.getRanking().getShortcutInfo() != null
- ? entry.getRanking().getShortcutInfo() : mShortcutInfo;
- mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
- }
if (entry.getBubbleMetadata() != null) {
- mFlags = entry.getBubbleMetadata().getFlags();
mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
- mIcon = entry.getBubbleMetadata().getIcon();
- mIntent = entry.getBubbleMetadata().getIntent();
- mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
}
}
- @Nullable
- Icon getIcon() {
- return mIcon;
- }
-
- boolean isVisuallyInterruptive() {
- return mIsVisuallyInterruptive;
- }
-
/**
* @return the last time this bubble was updated or accessed, whichever is most recent.
*/
@@ -394,19 +364,6 @@
return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
}
- public InstanceId getInstanceId() {
- return mInstanceId;
- }
-
- @Nullable
- public String getChannelId() {
- return mChannelId;
- }
-
- public int getNotificationId() {
- return mNotificationId;
- }
-
/**
* Should be invoked whenever a Bubble is accessed (selected while expanded).
*/
@@ -427,19 +384,24 @@
* Whether this notification should be shown in the shade.
*/
boolean showInShade() {
- return !shouldSuppressNotification() || !mIsClearable;
+ if (mEntry == null) return false;
+ return !shouldSuppressNotification() || !mEntry.isClearable();
}
/**
* Sets whether this notification should be suppressed in the shade.
*/
void setSuppressNotification(boolean suppressNotification) {
+ if (mEntry == null) return;
boolean prevShowInShade = showInShade();
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ int flags = data.getFlags();
if (suppressNotification) {
- mFlags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
} else {
- mFlags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
}
+ data.setFlags(flags);
if (showInShade() != prevShowInShade && mSuppressionListener != null) {
mSuppressionListener.onBubbleNotificationSuppressionChange(this);
@@ -462,8 +424,9 @@
*/
@Override
public boolean showDot() {
+ if (mEntry == null) return false;
return mShowBubbleUpdateDot
- && !mShouldSuppressNotificationDot
+ && !mEntry.shouldSuppressNotificationDot()
&& !shouldSuppressNotification();
}
@@ -471,9 +434,10 @@
* Whether the flyout for the bubble should be shown.
*/
boolean showFlyout() {
- return !mSuppressFlyout && !mShouldSuppressPeek
+ if (mEntry == null) return false;
+ return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
&& !shouldSuppressNotification()
- && !mShouldSuppressNotificationList;
+ && !mEntry.shouldSuppressNotificationList();
}
/**
@@ -516,14 +480,25 @@
}
}
- @Nullable
- PendingIntent getBubbleIntent() {
- return mIntent;
+ /**
+ * Whether shortcut information should be used to populate the bubble.
+ * <p>
+ * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
+ * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
+ */
+ boolean usingShortcutInfo() {
+ return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
+ || mShortcutInfo != null;
}
@Nullable
- PendingIntent getDeleteIntent() {
- return mDeleteIntent;
+ PendingIntent getBubbleIntent() {
+ if (mEntry == null) return null;
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ if (data != null) {
+ return data.getIntent();
+ }
+ return null;
}
Intent getSettingsIntent(final Context context) {
@@ -539,12 +514,8 @@
return intent;
}
- public int getAppUid() {
- return mAppUid;
- }
-
private int getUid(final Context context) {
- if (mAppUid != -1) return mAppUid;
+ if (mEntry != null) return mEntry.getSbn().getUid();
final PackageManager pm = context.getPackageManager();
if (pm == null) return -1;
try {
@@ -577,27 +548,24 @@
}
private boolean shouldSuppressNotification() {
- return isEnabled(Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
+ if (mEntry == null) return true;
+ return mEntry.getBubbleMetadata() != null
+ && mEntry.getBubbleMetadata().isNotificationSuppressed();
}
- public boolean shouldAutoExpand() {
- return isEnabled(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ boolean shouldAutoExpand() {
+ if (mEntry == null) return mShouldAutoExpand;
+ Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
+ return (metadata != null && metadata.getAutoExpandBubble()) || mShouldAutoExpand;
}
void setShouldAutoExpand(boolean shouldAutoExpand) {
- if (shouldAutoExpand) {
- enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- } else {
- disable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- }
- }
-
- public void setIsBubble(final boolean isBubble) {
- mIsBubble = isBubble;
+ mShouldAutoExpand = shouldAutoExpand;
}
public boolean isBubble() {
- return mIsBubble;
+ if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0;
+ return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0;
}
public void enable(int option) {
@@ -608,10 +576,6 @@
mFlags &= ~option;
}
- public boolean isEnabled(int option) {
- return (mFlags & option) != 0;
- }
-
@Override
public String toString() {
return "Bubble{" + mKey + '}';
@@ -646,24 +610,34 @@
@Override
public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
- SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
- mPackageName,
- mChannelId,
- mNotificationId,
- index,
- bubbleCount,
- action,
- normalX,
- normalY,
- showInShade(),
- false /* isOngoing (unused) */,
- false /* isAppForeground (unused) */);
- }
-
- @Nullable
- private static String getTitle(@NonNull final NotificationEntry e) {
- final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
- Notification.EXTRA_TITLE);
- return titleCharSeq == null ? null : titleCharSeq.toString();
+ if (this.getEntry() == null
+ || this.getEntry().getSbn() == null) {
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ null /* package name */,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ false /* unread bubble */,
+ false /* on-going bubble */,
+ false /* isAppForeground (unused) */);
+ } else {
+ StatusBarNotification notification = this.getEntry().getSbn();
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ index,
+ bubbleCount,
+ action,
+ normalX,
+ normalY,
+ this.showInShade(),
+ false /* isOngoing (unused) */,
+ false /* isAppForeground (unused) */);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index b2c5402..c4c5da4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -505,7 +505,8 @@
addNotifCallback(new NotifCallback() {
@Override
public void removeNotification(NotificationEntry entry, int reason) {
- mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason);
+ mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
+ reason);
}
@Override
@@ -636,13 +637,8 @@
mStackView.setExpandListener(mExpandListener);
}
- mStackView.setUnbubbleConversationCallback(key -> {
- final NotificationEntry entry =
- mNotificationEntryManager.getPendingOrActiveNotif(key);
- if (entry != null) {
- onUserChangedBubble(entry, false /* shouldBubble */);
- }
- });
+ mStackView.setUnbubbleConversationCallback(notificationEntry ->
+ onUserChangedBubble(notificationEntry, false /* shouldBubble */));
}
addToWindowManagerMaybe();
@@ -1028,7 +1024,10 @@
* @param entry the notification to change bubble state for.
* @param shouldBubble whether the notification should show as a bubble or not.
*/
- public void onUserChangedBubble(@NonNull final NotificationEntry entry, boolean shouldBubble) {
+ public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) {
+ if (entry == null) {
+ return;
+ }
NotificationChannel channel = entry.getChannel();
final String appPkg = entry.getSbn().getPackageName();
final int appUid = entry.getSbn().getUid();
@@ -1104,8 +1103,7 @@
mBubbleData.removeSuppressedSummary(groupKey);
// Remove any associated bubble children with the summary
- final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
- groupKey, mNotificationEntryManager);
+ final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
for (int i = 0; i < bubbleChildren.size(); i++) {
removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
}
@@ -1163,18 +1161,21 @@
private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
Objects.requireNonNull(b);
- b.setIsBubble(isBubble);
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(b.getKey());
- if (entry != null) {
+ if (isBubble) {
+ b.enable(FLAG_BUBBLE);
+ } else {
+ b.disable(FLAG_BUBBLE);
+ }
+ if (b.getEntry() != null) {
// Updating the entry to be a bubble will trigger our normal update flow
- setIsBubble(entry, isBubble, b.shouldAutoExpand());
+ setIsBubble(b.getEntry(), isBubble, b.shouldAutoExpand());
} else if (isBubble) {
- // If bubble doesn't exist, it's a persisted bubble so we need to add it to the
- // stack ourselves
+ // If we have no entry to update, it's a persisted bubble so
+ // we need to add it to the stack ourselves
Bubble bubble = mBubbleData.getOrCreateBubble(null, b /* persistedBubble */);
inflateAndAdd(bubble, bubble.shouldAutoExpand() /* suppressFlyout */,
!bubble.shouldAutoExpand() /* showInShade */);
+
}
}
@@ -1213,8 +1214,6 @@
if (reason == DISMISS_NOTIF_CANCEL) {
bubblesToBeRemovedFromRepository.add(bubble);
}
- final NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- bubble.getKey());
if (!mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
if (!mBubbleData.hasOverflowBubbleWithKey(bubble.getKey())
&& (!bubble.showInShade()
@@ -1223,27 +1222,26 @@
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
- if (entry != null) {
- cb.removeNotification(entry, REASON_CANCEL);
+ if (bubble.getEntry() != null) {
+ cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
}
}
} else {
if (bubble.isBubble()) {
setIsBubble(bubble, false /* isBubble */);
}
- if (entry != null && entry.getRow() != null) {
- entry.getRow().updateBubbleButton();
+ if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) {
+ bubble.getEntry().getRow().updateBubbleButton();
}
}
}
- if (entry != null) {
- final String groupKey = entry.getSbn().getGroupKey();
- if (mBubbleData.getBubblesInGroup(
- groupKey, mNotificationEntryManager).isEmpty()) {
+ if (bubble.getEntry() != null) {
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
// Time to potentially remove the summary
for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(entry);
+ cb.maybeCancelSummary(bubble.getEntry());
}
}
}
@@ -1268,12 +1266,9 @@
if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble);
- if (update.selectedBubble != null) {
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(update.selectedBubble.getKey());
- if (entry != null) {
- mNotificationGroupManager.updateSuppression(entry);
- }
+ if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
+ mNotificationGroupManager.updateSuppression(
+ update.selectedBubble.getEntry());
}
}
@@ -1346,8 +1341,7 @@
}
String groupKey = entry.getSbn().getGroupKey();
- ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
- groupKey, mNotificationEntryManager);
+ ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1367,15 +1361,9 @@
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
Bubble bubbleChild = mBubbleData.getAnyBubbleWithkey(child.getKey());
- if (bubbleChild != null) {
- final NotificationEntry entry = mNotificationEntryManager
- .getPendingOrActiveNotif(bubbleChild.getKey());
- if (entry != null) {
- mNotificationGroupManager.onEntryRemoved(entry);
- }
- bubbleChild.setSuppressNotification(true);
- bubbleChild.setShowDot(false /* show */);
- }
+ mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
+ bubbleChild.setSuppressNotification(true);
+ bubbleChild.setShowDot(false /* show */);
} else {
// non-bubbled children can be removed
for (NotifCallback cb : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index c870612..20a9a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -22,6 +22,7 @@
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.util.Log;
@@ -33,7 +34,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
@@ -256,7 +256,8 @@
}
mPendingBubbles.remove(bubble.getKey()); // No longer pending once we're here
Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
- suppressFlyout |= !bubble.isVisuallyInterruptive();
+ suppressFlyout |= bubble.getEntry() == null
+ || !bubble.getEntry().getRanking().visuallyInterruptive();
if (prevBubble == null) {
// Create a new bubble
@@ -334,15 +335,13 @@
* Retrieves any bubbles that are part of the notification group represented by the provided
* group key.
*/
- ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
- NotificationEntryManager nem) {
+ ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) {
ArrayList<Bubble> bubbleChildren = new ArrayList<>();
if (groupKey == null) {
return bubbleChildren;
}
for (Bubble b : mBubbles) {
- final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
- if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
+ if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
bubbleChildren.add(b);
}
}
@@ -440,7 +439,9 @@
Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected);
}
- maybeSendDeleteIntent(reason, bubbleToRemove);
+ if (bubbleToRemove.getEntry() != null) {
+ maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
+ }
}
void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -610,14 +611,21 @@
return true;
}
- private void maybeSendDeleteIntent(@DismissReason int reason, @NonNull final Bubble bubble) {
- if (reason != BubbleController.DISMISS_USER_GESTURE) return;
- PendingIntent deleteIntent = bubble.getDeleteIntent();
- if (deleteIntent == null) return;
- try {
- deleteIntent.send();
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to send delete intent for bubble with key: " + bubble.getKey());
+ private void maybeSendDeleteIntent(@DismissReason int reason,
+ @NonNull final NotificationEntry entry) {
+ if (reason == BubbleController.DISMISS_USER_GESTURE) {
+ Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
+ PendingIntent deleteIntent = bubbleMetadata != null
+ ? bubbleMetadata.getDeleteIntent()
+ : null;
+ if (deleteIntent != null) {
+ try {
+ deleteIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send delete intent for bubble with key: "
+ + entry.getKey());
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index 0c25d14..d20f405 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -74,15 +74,11 @@
private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
- BubbleEntity(
- userId,
- b.packageName,
- b.shortcutInfo?.id ?: return@mapNotNull null,
- b.key,
- b.rawDesiredHeight,
- b.rawDesiredHeightResId,
- b.title
- )
+ var shortcutId = b.shortcutInfo?.id
+ if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
+ if (shortcutId == null) return@mapNotNull null
+ BubbleEntity(userId, b.packageName, shortcutId, b.key, b.rawDesiredHeight,
+ b.rawDesiredHeightResId)
}
}
@@ -163,13 +159,8 @@
val bubbles = entities.mapNotNull { entity ->
shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
- ?.let { shortcutInfo -> Bubble(
- entity.key,
- shortcutInfo,
- entity.desiredHeight,
- entity.desiredHeightResId,
- entity.title
- ) }
+ ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo, entity.desiredHeight,
+ entity.desiredHeightResId) }
}
uiScope.launch { cb(bubbles) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 2f7ffde..ca359b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -169,7 +169,7 @@
return;
}
try {
- if (!mIsOverflow && mBubble.getShortcutInfo() != null) {
+ if (!mIsOverflow && mBubble.usingShortcutInfo()) {
options.setApplyActivityFlagsForBubbles(true);
mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
options, null /* sourceBounds */);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 1fa3aaa..8c76cda 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -31,7 +31,6 @@
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -224,10 +223,9 @@
float[] dotCenter,
boolean hideDot) {
- final Drawable senderAvatar = flyoutMessage.senderAvatar;
- if (senderAvatar != null && flyoutMessage.isGroupChat) {
+ if (flyoutMessage.senderAvatar != null && flyoutMessage.isGroupChat) {
mSenderAvatar.setVisibility(VISIBLE);
- mSenderAvatar.setImageDrawable(senderAvatar);
+ mSenderAvatar.setImageDrawable(flyoutMessage.senderAvatar);
} else {
mSenderAvatar.setVisibility(GONE);
mSenderAvatar.setTranslationX(0);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index a799f2d..74231c6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -15,8 +15,7 @@
*/
package com.android.systemui.bubbles;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
@@ -51,14 +50,15 @@
/**
* Returns the drawable that the developer has provided to display in the bubble.
*/
- Drawable getBubbleDrawable(@NonNull final Context context,
- @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
+ Drawable getBubbleDrawable(Context context, ShortcutInfo shortcutInfo,
+ Notification.BubbleMetadata metadata) {
if (shortcutInfo != null) {
LauncherApps launcherApps =
(LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
int density = context.getResources().getConfiguration().densityDpi;
return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
} else {
+ Icon ic = metadata.getIcon();
if (ic != null) {
if (ic.getType() == Icon.TYPE_URI
|| ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index c1dd8c3..c5faae0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.bubbles;
+import android.service.notification.StatusBarNotification;
+
import com.android.internal.logging.UiEventLoggerImpl;
/**
@@ -30,11 +32,12 @@
* @param e UI event
*/
public void log(Bubble b, UiEventEnum e) {
- if (b.getInstanceId() == null) {
+ if (b.getEntry() == null) {
// Added from persistence -- TODO log this with specific event?
return;
}
- logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
+ StatusBarNotification sbn = b.getEntry().getSbn();
+ logWithInstanceId(e, sbn.getUid(), sbn.getPackageName(), sbn.getInstanceId());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 0b25c44..b644079 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -291,7 +291,9 @@
});
// If the bubble was persisted, the entry is null but it should have shortcut info
- ShortcutInfo info = b.getShortcutInfo();
+ ShortcutInfo info = b.getEntry() == null
+ ? b.getShortcutInfo()
+ : b.getEntry().getRanking().getShortcutInfo();
if (info == null) {
Log.d(TAG, "ShortcutInfo required to bubble but none found for " + b);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0e6ce5c..23bd174 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -92,6 +92,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -293,7 +294,7 @@
private BubbleController.BubbleExpandListener mExpandListener;
/** Callback to run when we want to unbubble the given notification's conversation. */
- private Consumer<String> mUnbubbleConversationCallback;
+ private Consumer<NotificationEntry> mUnbubbleConversationCallback;
private SysUiState mSysUiState;
@@ -1013,7 +1014,10 @@
mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener(
view -> {
showManageMenu(false /* show */);
- mUnbubbleConversationCallback.accept(mBubbleData.getSelectedBubble().getKey());
+ final Bubble bubble = mBubbleData.getSelectedBubble();
+ if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+ mUnbubbleConversationCallback.accept(bubble.getEntry());
+ }
});
mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener(
@@ -1365,7 +1369,7 @@
/** Sets the function to call to un-bubble the given conversation. */
public void setUnbubbleConversationCallback(
- Consumer<String> unbubbleConversationCallback) {
+ Consumer<NotificationEntry> unbubbleConversationCallback) {
mUnbubbleConversationCallback = unbubbleConversationCallback;
}
@@ -1905,7 +1909,9 @@
updateOverflowVisibility();
afterExpandedViewAnimation();
- previouslySelected.setContentVisibility(false);
+ if (previouslySelected != null) {
+ previouslySelected.setContentVisibility(false);
+ }
})
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 3e4ff52..525d5b5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -37,6 +37,8 @@
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Parcelable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
import android.util.PathParser;
@@ -51,7 +53,6 @@
import java.lang.ref.WeakReference;
import java.util.List;
-import java.util.Objects;
/**
* Simple task to inflate views & load necessary info to display a bubble.
@@ -128,10 +129,35 @@
@Nullable
static BubbleViewInfo populate(Context c, BubbleStackView stackView,
BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
+ final NotificationEntry entry = b.getEntry();
+ if (entry == null) {
+ // populate from ShortcutInfo when NotificationEntry is not available
+ final ShortcutInfo s = b.getShortcutInfo();
+ return populate(c, stackView, iconFactory, skipInflation || b.isInflated(),
+ s.getPackage(), s.getUserHandle(), s, null);
+ }
+ final StatusBarNotification sbn = entry.getSbn();
+ final String bubbleShortcutId = entry.getBubbleMetadata().getShortcutId();
+ final ShortcutInfo si = bubbleShortcutId == null
+ ? null : entry.getRanking().getShortcutInfo();
+ return populate(
+ c, stackView, iconFactory, skipInflation || b.isInflated(),
+ sbn.getPackageName(), sbn.getUser(), si, entry);
+ }
+
+ private static BubbleViewInfo populate(
+ @NonNull final Context c,
+ @NonNull final BubbleStackView stackView,
+ @NonNull final BubbleIconFactory iconFactory,
+ final boolean isInflated,
+ @NonNull final String packageName,
+ @NonNull final UserHandle user,
+ @Nullable final ShortcutInfo shortcutInfo,
+ @Nullable final NotificationEntry entry) {
BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble
- if (!skipInflation && !b.isInflated()) {
+ if (!isInflated) {
LayoutInflater inflater = LayoutInflater.from(c);
info.imageView = (BadgedImageView) inflater.inflate(
R.layout.bubble_view, stackView, false /* attachToRoot */);
@@ -141,8 +167,8 @@
info.expandedView.setStackView(stackView);
}
- if (b.getShortcutInfo() != null) {
- info.shortcutInfo = b.getShortcutInfo();
+ if (shortcutInfo != null) {
+ info.shortcutInfo = shortcutInfo;
}
// App name & app icon
@@ -152,7 +178,7 @@
Drawable appIcon;
try {
appInfo = pm.getApplicationInfo(
- b.getPackageName(),
+ packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -160,17 +186,17 @@
if (appInfo != null) {
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
- appIcon = pm.getApplicationIcon(b.getPackageName());
- badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
+ appIcon = pm.getApplicationIcon(packageName);
+ badgedIcon = pm.getUserBadgedIcon(appIcon, user);
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ Log.w(TAG, "Unable to find package: " + packageName);
return null;
}
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
- b.getIcon());
+ entry == null ? null : entry.getBubbleMetadata());
if (bubbleDrawable == null) {
// Default to app icon
bubbleDrawable = appIcon;
@@ -196,10 +222,8 @@
Color.WHITE, WHITE_SCRIM_ALPHA);
// Flyout
- info.flyoutMessage = b.getFlyoutMessage();
- if (info.flyoutMessage != null) {
- info.flyoutMessage.senderAvatar =
- loadSenderAvatar(c, info.flyoutMessage.senderIcon);
+ if (entry != null) {
+ info.flyoutMessage = extractFlyoutMessage(c, entry);
}
return info;
}
@@ -211,8 +235,8 @@
* notification, based on its type. Returns null if there should not be an update message.
*/
@NonNull
- static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
- Objects.requireNonNull(entry);
+ static Bubble.FlyoutMessage extractFlyoutMessage(Context context,
+ NotificationEntry entry) {
final Notification underlyingNotif = entry.getSbn().getNotification();
final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
@@ -240,9 +264,20 @@
if (latestMessage != null) {
bubbleMessage.message = latestMessage.getText();
Person sender = latestMessage.getSenderPerson();
- bubbleMessage.senderName = sender != null ? sender.getName() : null;
+ bubbleMessage.senderName = sender != null
+ ? sender.getName()
+ : null;
+
bubbleMessage.senderAvatar = null;
- bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
+ if (sender != null && sender.getIcon() != null) {
+ if (sender.getIcon().getType() == Icon.TYPE_URI
+ || sender.getIcon().getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ context.grantUriPermission(context.getPackageName(),
+ sender.getIcon().getUri(),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ bubbleMessage.senderAvatar = sender.getIcon().loadDrawable(context);
+ }
return bubbleMessage;
}
} else if (Notification.InboxStyle.class.equals(style)) {
@@ -271,15 +306,4 @@
return bubbleMessage;
}
-
- @Nullable
- static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
- Objects.requireNonNull(context);
- if (icon == null) return null;
- if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
- context.grantUriPermission(context.getPackageName(),
- icon.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- return icon.loadDrawable(context);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
index 24768cd..355c4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleEntity.kt
@@ -24,6 +24,5 @@
val shortcutId: String,
val key: String,
val desiredHeight: Int,
- @DimenRes val desiredHeightResId: Int,
- val title: String? = null
+ @DimenRes val desiredHeightResId: Int
)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
index 66fff338..a8faf25 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleXmlHelper.kt
@@ -33,7 +33,6 @@
private const val ATTR_KEY = "key"
private const val ATTR_DESIRED_HEIGHT = "h"
private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
-private const val ATTR_TITLE = "t"
/**
* Writes the bubbles in xml format into given output stream.
@@ -64,7 +63,6 @@
serializer.attribute(null, ATTR_KEY, bubble.key)
serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
- bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -94,8 +92,7 @@
parser.getAttributeWithName(ATTR_SHORTCUT_ID) ?: return null,
parser.getAttributeWithName(ATTR_KEY) ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
- parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
- parser.getAttributeWithName(ATTR_TITLE)
+ parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
rename to packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
index 693c270..d2776d2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.util
+package com.android.systemui.controls
import android.os.UserHandle
@@ -23,8 +23,6 @@
* changes.
*/
interface UserAwareController {
- @JvmDefault
fun changeUser(newUser: UserHandle) {}
-
val currentUserId: Int
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index eed5531..d4d4d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -20,7 +20,7 @@
import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.service.controls.actions.ControlAction
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
import java.util.function.Consumer
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 496741b..45ba1e6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -21,7 +21,7 @@
import android.service.controls.ControlsProviderService
import android.service.controls.actions.ControlAction
import com.android.systemui.controls.ControlStatus
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
import com.android.systemui.controls.management.ControlsFavoritingActivity
import com.android.systemui.controls.ui.ControlsUiController
import java.util.function.Consumer
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 26124f7..0543319 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -238,7 +238,7 @@
updateFavorite(!favorite.isChecked)
favoriteCallback(wrapper.controlId, favorite.isChecked)
}
- applyRenderInfo(renderInfo)
+ applyRenderInfo(renderInfo, wrapper.deviceType)
}
override fun updateFavorite(favorite: Boolean) {
@@ -254,12 +254,16 @@
return RenderInfo.lookup(itemView.context, component, deviceType)
}
- private fun applyRenderInfo(ri: RenderInfo) {
+ private fun applyRenderInfo(ri: RenderInfo, @DeviceTypes.DeviceType deviceType: Int) {
val context = itemView.context
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
icon.setImageDrawable(ri.icon)
- icon.setImageTintList(fg)
+
+ // Do not color app icons
+ if (deviceType != DeviceTypes.TYPE_ROUTINE) {
+ icon.setImageTintList(fg)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index b9f1666..647dacc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,7 +18,7 @@
import android.content.ComponentName
import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.util.UserAwareController
+import com.android.systemui.controls.UserAwareController
import com.android.systemui.statusbar.policy.CallbackController
/**
@@ -26,7 +26,7 @@
*/
interface ControlsListingController :
CallbackController<ControlsListingController.ControlsListingCallback>,
- UserAwareController {
+ UserAwareController {
/**
* @return the current list of services that satisfies the [ServiceListing].
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index ee02b85..e853027 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -274,7 +274,6 @@
val ri = RenderInfo.lookup(context, cws.componentName, deviceTypeOrError, offset)
val fg = context.resources.getColorStateList(ri.foreground, context.theme)
val newText = nextStatusText
- nextStatusText = ""
val control = cws.control
var shouldAnimate = animated
@@ -297,10 +296,8 @@
if (immediately) {
status.alpha = STATUS_ALPHA_ENABLED
status.text = text
- nextStatusText = ""
- } else {
- nextStatusText = text
}
+ nextStatusText = text
}
private fun animateBackgroundChange(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
index 48f9458..9dd0f53 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
@@ -21,6 +21,7 @@
import android.view.View
import android.service.controls.Control
import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.StatelessTemplate
import com.android.systemui.R
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
@@ -35,24 +36,44 @@
lateinit var template: ControlTemplate
lateinit var control: Control
lateinit var cvh: ControlViewHolder
+ private var statelessTouch = false
+ private var lastColorOffset = 0
+ private val enabled: Boolean
+ get() = if (lastColorOffset > 0 || statelessTouch) true else false
+
+ companion object {
+ const val STATELESS_ENABLE_TIMEOUT_IN_MILLIS = 3000L
+ }
override fun initialize(cvh: ControlViewHolder) {
this.cvh = cvh
cvh.layout.setOnClickListener(View.OnClickListener() {
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
+
+ // StatelessTemplates have no state, with no way to discern between enabled and
+ // disabled. Render an enabled state for a few moments to let the user know the
+ // action is in progress.
+ if (template is StatelessTemplate) {
+ statelessTouch = true
+ cvh.applyRenderInfo(enabled, lastColorOffset)
+ cvh.uiExecutor.executeDelayed({
+ statelessTouch = false
+ cvh.applyRenderInfo(enabled, lastColorOffset)
+ }, STATELESS_ENABLE_TIMEOUT_IN_MILLIS)
+ }
})
}
override fun bind(cws: ControlWithState, colorOffset: Int) {
this.control = cws.control!!
+ lastColorOffset = colorOffset
cvh.setStatusText(control.getStatusText())
template = control.getControlTemplate()
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
- val enabled = if (colorOffset > 0) true else false
clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
cvh.applyRenderInfo(enabled, colorOffset)
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index a2086e8..16f76de 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -68,7 +68,7 @@
public static LogBuffer provideNotificationSectionLogBuffer(
LogcatEchoTracker bufferFilter,
DumpManager dumpManager) {
- LogBuffer buffer = new LogBuffer("NotifSectionLog", 500, 10, bufferFilter);
+ LogBuffer buffer = new LogBuffer("NotifSectionLog", 1000, 10, bufferFilter);
buffer.attach(dumpManager);
return buffer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 5008100..d5a2837 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -257,16 +257,18 @@
rect.setColor(Color.TRANSPARENT);
final MediaDeviceData device = data.getDevice();
+ int seamlessId = mViewHolder.getSeamless().getId();
if (device != null && !device.getEnabled()) {
mViewHolder.getSeamless().setEnabled(false);
- // TODO(b/156875717): setEnabled should cause the alpha to change.
- mViewHolder.getSeamless().setAlpha(0.38f);
+ expandedSet.setAlpha(seamlessId, 0.38f);
+ collapsedSet.setAlpha(seamlessId, 0.38f);
iconView.setImageResource(R.drawable.ic_hardware_speaker);
iconView.setVisibility(View.VISIBLE);
deviceName.setText(R.string.media_seamless_remote_device);
} else if (device != null) {
mViewHolder.getSeamless().setEnabled(true);
- mViewHolder.getSeamless().setAlpha(1f);
+ expandedSet.setAlpha(seamlessId, 1.0f);
+ collapsedSet.setAlpha(seamlessId, 1.0f);
Drawable icon = device.getIcon();
iconView.setVisibility(View.VISIBLE);
@@ -282,7 +284,8 @@
// Reset to default
Log.w(TAG, "device is null. Not binding output chip.");
mViewHolder.getSeamless().setEnabled(true);
- mViewHolder.getSeamless().setAlpha(1f);
+ expandedSet.setAlpha(seamlessId, 1.0f);
+ collapsedSet.setAlpha(seamlessId, 1.0f);
iconView.setVisibility(View.GONE);
deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index d077666..856c192 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -491,10 +491,11 @@
Log.d(TAG, "resizeAndAnimatePipUnchecked: toBounds=" + toBounds
+ " duration=" + duration + " callers=\n" + Debug.getCallers(5, " "));
}
- if (!toBounds.equals(mBounds)) {
- mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
- setAnimatingToBounds(toBounds);
- }
+
+ // Intentionally resize here even if the current bounds match the destination bounds.
+ // This is so all the proper callbacks are performed.
+ mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
+ setAnimatingToBounds(toBounds);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index f6b212c..c151715 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -260,12 +260,14 @@
private void onMotionEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ float x = ev.getX();
+ float y = ev.getY();
if (action == MotionEvent.ACTION_DOWN) {
mLastResizeBounds.setEmpty();
- mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+ mAllowGesture = isWithinTouchRegion((int) x, (int) y);
if (mAllowGesture) {
- setCtrlType((int) ev.getX(), (int) ev.getY());
- mDownPoint.set(ev.getX(), ev.getY());
+ setCtrlType((int) x, (int) y);
+ mDownPoint.set(x, y);
mLastDownBounds.set(mMotionHelper.getBounds());
}
@@ -277,20 +279,23 @@
break;
case MotionEvent.ACTION_MOVE:
// Capture inputs
- float dx = Math.abs(ev.getX() - mDownPoint.x);
- float dy = Math.abs(ev.getY() - mDownPoint.y);
- if (!mThresholdCrossed && dx > mTouchSlop && dy > mTouchSlop) {
+ if (!mThresholdCrossed
+ && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
mThresholdCrossed = true;
+ // Reset the down to begin resizing from this point
+ mDownPoint.set(x, y);
mInputMonitor.pilferPointers();
}
- final Rect currentPipBounds = mMotionHelper.getBounds();
- mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
- mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
- mMinSize.y, mMaxSize, true,
- mLastDownBounds.width() > mLastDownBounds.height()));
- mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
- mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
- null);
+ if (mThresholdCrossed) {
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true,
+ mLastDownBounds.width() > mLastDownBounds.height()));
+ mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+ mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
+ null);
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
index 0a84f5e..2365e67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
-import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -31,7 +30,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
-import com.android.systemui.util.UserAwareController;
import java.util.Arrays;
import java.util.Collection;
@@ -39,7 +37,7 @@
import javax.inject.Inject;
-public class AutoAddTracker implements UserAwareController {
+public class AutoAddTracker {
private static final String[][] CONVERT_PREFS = {
{Key.QS_HOTSPOT_ADDED, HOTSPOT},
@@ -51,39 +49,20 @@
private final ArraySet<String> mAutoAdded;
private final Context mContext;
- private int mUserId;
- public AutoAddTracker(Context context, int userId) {
+ @Inject
+ public AutoAddTracker(Context context) {
mContext = context;
- mUserId = userId;
mAutoAdded = new ArraySet<>(getAdded());
// TODO: remove migration code and shared preferences keys after P release
- if (mUserId == UserHandle.USER_SYSTEM) {
- for (String[] convertPref : CONVERT_PREFS) {
- if (Prefs.getBoolean(context, convertPref[0], false)) {
- setTileAdded(convertPref[1]);
- Prefs.remove(context, convertPref[0]);
- }
+ for (String[] convertPref : CONVERT_PREFS) {
+ if (Prefs.getBoolean(context, convertPref[0], false)) {
+ setTileAdded(convertPref[1]);
+ Prefs.remove(context, convertPref[0]);
}
}
mContext.getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
- UserHandle.USER_ALL);
- }
-
- @Override
- public void changeUser(UserHandle newUser) {
- if (newUser.getIdentifier() == mUserId) {
- return;
- }
- mUserId = newUser.getIdentifier();
- mAutoAdded.clear();
- mAutoAdded.addAll(getAdded());
- }
-
- @Override
- public int getCurrentUserId() {
- return mUserId;
+ Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver);
}
public boolean isAdded(String tile) {
@@ -107,13 +86,12 @@
}
private void saveTiles() {
- Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
- TextUtils.join(",", mAutoAdded), mUserId);
+ Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
+ TextUtils.join(",", mAutoAdded));
}
private Collection<String> getAdded() {
- String current = Secure.getStringForUser(mContext.getContentResolver(),
- Secure.QS_AUTO_ADDED_TILES, mUserId);
+ String current = Secure.getString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES);
if (current == null) {
return Collections.emptyList();
}
@@ -124,27 +102,7 @@
protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
- mAutoAdded.clear();
mAutoAdded.addAll(getAdded());
}
};
-
- public static class Builder {
- private final Context mContext;
- private int mUserId;
-
- @Inject
- public Builder(Context context) {
- mContext = context;
- }
-
- public Builder setUserId(int userId) {
- mUserId = userId;
- return this;
- }
-
- public AutoAddTracker build() {
- return new AutoAddTracker(mContext, mUserId);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 2836992..3eed8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -291,15 +291,7 @@
}
private void emptyAndInflateOrRemovePages() {
- final int nTiles = mTiles.size();
- // We should always have at least one page, even if it's empty.
- int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
-
- // Add one more not full page if needed
- if (nTiles > numPages * mPages.get(0).maxTiles()) {
- numPages++;
- }
-
+ final int numPages = getNumPages();
final int NP = mPages.size();
for (int i = 0; i < NP; i++) {
mPages.get(i).removeAllViews();
@@ -431,6 +423,22 @@
return mPages.get(0).mColumns;
}
+ /**
+ * Gets the number of pages in this paged tile layout
+ */
+ public int getNumPages() {
+ final int nTiles = mTiles.size();
+ // We should always have at least one page, even if it's empty.
+ int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
+
+ // Add one more not full page if needed
+ if (nTiles > numPages * mPages.get(0).maxTiles()) {
+ numPages++;
+ }
+
+ return numPages;
+ }
+
public int getNumVisibleTiles() {
if (mPages.size() == 0) return 0;
TilePage currentPage = mPages.get(getCurrentPageNumber());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index fc8e36f..c4bb4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -218,7 +218,7 @@
.addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mEditContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
- .setStartDelay(0.15f)
+ .setStartDelay(0.9f)
.build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 32f4b6c..d3f59ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -240,8 +240,13 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
if (mTileLayout instanceof PagedTileLayout) {
+ // Since PageIndicator gets measured before PagedTileLayout, we preemptively set the
+ // # of pages before the measurement pass so PageIndicator is measured appropriately
+ if (mFooterPageIndicator != null) {
+ mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages());
+ }
+
// Allow the UI to be as big as it want's to, we're in a scroll view
int newHeight = 10000;
int availableHeight = MeasureSpec.getSize(heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 858a7e2..65d3572 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -255,9 +255,6 @@
int currentUser = ActivityManager.getCurrentUser();
if (currentUser != mCurrentUser) {
mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
- if (mAutoTiles != null) {
- mAutoTiles.changeUser(UserHandle.of(currentUser));
- }
}
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 311eda2..b5afe77 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -15,6 +15,7 @@
package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
@@ -41,6 +42,7 @@
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.ImageView;
import android.widget.RelativeLayout;
@@ -347,6 +349,15 @@
com.android.internal.R.dimen.quick_qs_offset_height);
mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+ ViewGroup.LayoutParams lp = getLayoutParams();
+ if (mQsDisabled) {
+ lp.height = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
+ } else {
+ lp.height = WRAP_CONTENT;
+ }
+ setLayoutParams(lp);
+
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index f3e2f10..f89185e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -118,6 +118,7 @@
try {
if (mOverviewProxyService.getProxy() != null) {
mOverviewProxyService.getProxy().onOverviewToggle();
+ mOverviewProxyService.notifyToggleRecentApps();
}
} catch (RemoteException e) {
Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 8a012b8..790b2585 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -200,11 +200,13 @@
mInputFocusTransferStartY = event.getY();
mInputFocusTransferStartMillis = event.getEventTime();
statusBar.onInputFocusTransfer(
- mInputFocusTransferStarted, 0 /* velocity */);
+ mInputFocusTransferStarted, false /* cancel */,
+ 0 /* velocity */);
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInputFocusTransferStarted = false;
statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
+ action == ACTION_CANCEL,
(event.getY() - mInputFocusTransferStartY)
/ (event.getEventTime() - mInputFocusTransferStartMillis));
}
@@ -692,7 +694,8 @@
mHandler.post(()-> {
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
mInputFocusTransferStarted = false;
- statusBarLazy.get().onInputFocusTransfer(false, 0 /* velocity */);
+ statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
+ 0 /* velocity */);
});
});
}
@@ -871,6 +874,12 @@
}
}
+ void notifyToggleRecentApps() {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onToggleRecentApps();
+ }
+ }
+
private void updateEnabledState() {
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
MATCH_SYSTEM_ONLY,
@@ -901,6 +910,8 @@
default void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {}
default void onOverviewShown(boolean fromHome) {}
default void onQuickScrubStarted() {}
+ /** Notify the recents app (overview) is started by 3-button navigation. */
+ default void onToggleRecentApps() {}
/** Notify changes in the nav bar button alpha */
default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7babe2f..ca37532 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -215,7 +215,6 @@
private Animator mScreenshotAnimation;
private Runnable mOnCompleteRunnable;
private Animator mDismissAnimation;
- private SavedImageData mImageData;
private boolean mInDarkMode = false;
private boolean mDirectionLTR = true;
private boolean mOrientationPortrait = true;
@@ -234,9 +233,6 @@
switch (msg.what) {
case MESSAGE_CORNER_TIMEOUT:
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
- if (mImageData != null) {
- mNotificationsController.showSilentScreenshotNotification(mImageData);
- }
GlobalScreenshot.this.dismissScreenshot("timeout", false);
mOnCompleteRunnable.run();
break;
@@ -408,9 +404,6 @@
mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
mDismissButton.setOnClickListener(view -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
- if (mImageData != null) {
- mNotificationsController.showSilentScreenshotNotification(mImageData);
- }
dismissScreenshot("dismiss_button", false);
mOnCompleteRunnable.run();
});
@@ -450,10 +443,6 @@
});
}
- mImageData = null; // make sure we clear the current stored data
- mNotificationsController.reset();
- mNotificationsController.setImage(mScreenBitmap);
-
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
mSaveInBgTask.execute();
}
@@ -670,7 +659,6 @@
*/
private void showUiOnActionsReady(SavedImageData imageData) {
logSuccessOnActionsReady(imageData);
- mImageData = imageData;
AccessibilityManager accessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index e4e253e..fbcd6ba 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -148,11 +148,43 @@
}
/**
- * Shows a silent notification with the saved screenshot and actions that can be taken with it.
+ * Shows a notification to inform the user that a screenshot is currently being saved.
+ */
+ public void showSavingScreenshotNotification() {
+ final long now = System.currentTimeMillis();
+
+ mPublicNotificationBuilder
+ .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setCategory(Notification.CATEGORY_PROGRESS)
+ .setWhen(now)
+ .setShowWhen(true)
+ .setColor(mResources.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
+ SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
+
+ mNotificationBuilder
+ .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
+ .setSmallIcon(R.drawable.stat_notify_image)
+ .setWhen(now)
+ .setShowWhen(true)
+ .setColor(mResources.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setStyle(mNotificationStyle)
+ .setPublicVersion(mPublicNotificationBuilder.build());
+ mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
+ SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
+
+ mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
+ mNotificationBuilder.build());
+ }
+
+ /**
+ * Shows a notification with the saved screenshot and actions that can be taken with it.
*
* @param actionData SavedImageData struct with image URI and actions
*/
- public void showSilentScreenshotNotification(
+ public void showScreenshotActionsNotification(
GlobalScreenshot.SavedImageData actionData) {
mNotificationBuilder.addAction(actionData.shareAction);
mNotificationBuilder.addAction(actionData.editAction);
@@ -174,34 +206,20 @@
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
- .setShowWhen(true)
.setAutoCancel(true)
.setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setGroup("silent")
- .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
+ com.android.internal.R.color.system_notification_accent_color));
mNotificationBuilder
.setContentTitle(mResources.getString(R.string.screenshot_saved_title))
.setContentText(mResources.getString(R.string.screenshot_saved_text))
.setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
.setWhen(now)
- .setShowWhen(true)
.setAutoCancel(true)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setPublicVersion(mPublicNotificationBuilder.build())
- .setStyle(mNotificationStyle)
- .setFlag(Notification.FLAG_NO_CLEAR, false)
- .setGroup("silent")
- .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY);
-
- SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
- SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
+ .setFlag(Notification.FLAG_NO_CLEAR, false);
mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
mNotificationBuilder.build());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 1669345..21810c0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -253,9 +253,7 @@
mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
mRotateSplitLayout = null;
}
- if (isSplitActive()) {
- update(newConfig);
- }
+ update(newConfig);
}
Handler getHandler() {
@@ -330,6 +328,11 @@
mHandler.post(this::removeDivider);
}
+ void onTasksReady() {
+ mHandler.post(() -> update(mDisplayController.getDisplayContext(
+ mContext.getDisplayId()).getResources().getConfiguration()));
+ }
+
private void updateVisibility(final boolean visible) {
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
if (mVisible != visible) {
@@ -521,7 +524,7 @@
void ensureMinimizedSplit() {
setHomeMinimized(true /* minimized */, mSplits.mSecondary.isResizable());
- if (mView != null && !isDividerVisible()) {
+ if (!isDividerVisible()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
Slog.d(TAG, " entering split mode with minimized=true");
@@ -532,7 +535,7 @@
void ensureNormalSplit() {
setHomeMinimized(false /* minimized */, mHomeStackResizable);
- if (mView != null && !isDividerVisible()) {
+ if (!isDividerVisible()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
Slog.d(TAG, " enter split mode unminimized ");
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 44674df..db32482 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -113,6 +113,8 @@
t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
t.apply();
releaseTransaction(t);
+
+ mDivider.onTasksReady();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index ad04788..bd0d0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -29,6 +29,7 @@
import androidx.annotation.Nullable;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -36,6 +37,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -58,6 +60,7 @@
*/
public class NotificationLogger implements StateListener {
private static final String TAG = "NotificationLogger";
+ private static final boolean DEBUG = false;
/** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
@@ -79,7 +82,12 @@
private long mLastVisibilityReportUptimeMs;
private NotificationListContainer mListContainer;
private final Object mDozingLock = new Object();
- private boolean mDozing;
+ @GuardedBy("mDozingLock")
+ private Boolean mDozing = null; // Use null to indicate state is not yet known
+ @GuardedBy("mDozingLock")
+ private Boolean mLockscreen = null; // Use null to indicate state is not yet known
+ private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known
+ private boolean mLogging = false;
protected final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() {
@@ -247,33 +255,44 @@
}
public void stopNotificationLogging() {
- // Report all notifications as invisible and turn down the
- // reporter.
- if (!mCurrentlyVisibleNotifications.isEmpty()) {
- logNotificationVisibilityChanges(
- Collections.emptyList(), mCurrentlyVisibleNotifications);
- recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+ if (mLogging) {
+ mLogging = false;
+ if (DEBUG) {
+ Log.i(TAG, "stopNotificationLogging: log notifications invisible");
+ }
+ // Report all notifications as invisible and turn down the
+ // reporter.
+ if (!mCurrentlyVisibleNotifications.isEmpty()) {
+ logNotificationVisibilityChanges(
+ Collections.emptyList(), mCurrentlyVisibleNotifications);
+ recycleAllVisibilityObjects(mCurrentlyVisibleNotifications);
+ }
+ mHandler.removeCallbacks(mVisibilityReporter);
+ mListContainer.setChildLocationsChangedListener(null);
}
- mHandler.removeCallbacks(mVisibilityReporter);
- mListContainer.setChildLocationsChangedListener(null);
}
public void startNotificationLogging() {
- mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
- // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
- // cause the scroller to emit child location events. Hence generate
- // one ourselves to guarantee that we're reporting visible
- // notifications.
- // (Note that in cases where the scroller does emit events, this
- // additional event doesn't break anything.)
- mNotificationLocationsChangedListener.onChildLocationsChanged();
- mNotificationPanelLogger.logPanelShown(mListContainer.hasPulsingNotifications(),
- mEntryManager.getVisibleNotifications());
+ if (!mLogging) {
+ mLogging = true;
+ if (DEBUG) {
+ Log.i(TAG, "startNotificationLogging");
+ }
+ mListContainer.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
+ // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
+ // cause the scroller to emit child location events. Hence generate
+ // one ourselves to guarantee that we're reporting visible
+ // notifications.
+ // (Note that in cases where the scroller does emit events, this
+ // additional event doesn't break anything.)
+ mNotificationLocationsChangedListener.onChildLocationsChanged();
+ }
}
private void setDozing(boolean dozing) {
synchronized (mDozingLock) {
mDozing = dozing;
+ maybeUpdateLoggingStatus();
}
}
@@ -343,19 +362,12 @@
for (int i = 0; i < N; i++) {
newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
}
-
- synchronized (mDozingLock) {
- // setNotificationsShown should only be called if we are confident that
- // the user has seen the notification, aka not when ambient display is on
- if (!mDozing) {
- // TODO: Call NotificationEntryManager to do this, once it exists.
- // TODO: Consider not catching all runtime exceptions here.
- try {
- mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
- } catch (RuntimeException e) {
- Log.d(TAG, "failed setNotificationsShown: ", e);
- }
- }
+ // TODO: Call NotificationEntryManager to do this, once it exists.
+ // TODO: Consider not catching all runtime exceptions here.
+ try {
+ mNotificationListener.setNotificationsShown(newlyVisibleKeyAr);
+ } catch (RuntimeException e) {
+ Log.d(TAG, "failed setNotificationsShown: ", e);
}
}
recycleAllVisibilityObjects(newlyVisibleAr);
@@ -400,14 +412,64 @@
@Override
public void onStateChanged(int newState) {
- // don't care about state change
+ if (DEBUG) {
+ Log.i(TAG, "onStateChanged: new=" + newState);
+ }
+ synchronized (mDozingLock) {
+ mLockscreen = (newState == StatusBarState.KEYGUARD
+ || newState == StatusBarState.SHADE_LOCKED);
+ }
}
@Override
public void onDozingChanged(boolean isDozing) {
+ if (DEBUG) {
+ Log.i(TAG, "onDozingChanged: new=" + isDozing);
+ }
setDozing(isDozing);
}
+ @GuardedBy("mDozingLock")
+ private void maybeUpdateLoggingStatus() {
+ if (mPanelExpanded == null || mDozing == null) {
+ if (DEBUG) {
+ Log.i(TAG, "Panel status unclear: panelExpandedKnown="
+ + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null));
+ }
+ return;
+ }
+ // Once we know panelExpanded and Dozing, turn logging on & off when appropriate
+ boolean lockscreen = mLockscreen == null ? false : mLockscreen;
+ if (mPanelExpanded && !mDozing) {
+ mNotificationPanelLogger.logPanelShown(lockscreen,
+ mEntryManager.getVisibleNotifications());
+ if (DEBUG) {
+ Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen);
+ }
+ startNotificationLogging();
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen);
+ }
+ stopNotificationLogging();
+ }
+ }
+
+ /**
+ * Called by StatusBar to notify the logger that the panel expansion has changed.
+ * The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
+ * @param isExpanded True if the panel is expanded.
+ */
+ public void onPanelExpandedChanged(boolean isExpanded) {
+ if (DEBUG) {
+ Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
+ }
+ mPanelExpanded = isExpanded;
+ synchronized (mDozingLock) {
+ maybeUpdateLoggingStatus();
+ }
+ }
+
/**
* Called when the notification is expanded / collapsed.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 0bdac39..c87b998 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.children
+import com.android.systemui.util.takeUntil
import com.android.systemui.util.foldToSparseArray
import javax.inject.Inject
@@ -197,7 +198,7 @@
else -> null
}
- private fun logShadeContents() = parent.children.forEachIndexed { i, child ->
+ private fun logShadeChild(i: Int, child: View) {
when {
child === incomingHeaderView -> logger.logIncomingHeader(i)
child === mediaControlsView -> logger.logMediaControls(i)
@@ -216,6 +217,7 @@
}
}
}
+ private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild)
private val isUsingMultipleSections: Boolean
get() = sectionsFeatureManager.getNumberOfBuckets() > 1
@@ -223,6 +225,57 @@
@VisibleForTesting
fun updateSectionBoundaries() = updateSectionBoundaries("test")
+ private interface SectionUpdateState<out T : ExpandableView> {
+ val header: T
+ var currentPosition: Int?
+ var targetPosition: Int?
+ fun adjustViewPosition()
+ }
+
+ private fun <T : ExpandableView> expandableViewHeaderState(header: T): SectionUpdateState<T> =
+ object : SectionUpdateState<T> {
+ override val header = header
+ override var currentPosition: Int? = null
+ override var targetPosition: Int? = null
+
+ override fun adjustViewPosition() {
+ val target = targetPosition
+ val current = currentPosition
+ if (target == null) {
+ if (current != null) {
+ parent.removeView(header)
+ }
+ } else {
+ if (current == null) {
+ // If the header is animating away, it will still have a parent, so
+ // detach it first
+ // TODO: We should really cancel the active animations here. This will
+ // happen automatically when the view's intro animation starts, but
+ // it's a fragile link.
+ header.transientContainer?.removeTransientView(header)
+ header.transientContainer = null
+ parent.addView(header, target)
+ } else {
+ parent.changeViewPosition(header, target)
+ }
+ }
+ }
+ }
+
+ private fun <T : StackScrollerDecorView> decorViewHeaderState(
+ header: T
+ ): SectionUpdateState<T> {
+ val inner = expandableViewHeaderState(header)
+ return object : SectionUpdateState<T> by inner {
+ override fun adjustViewPosition() {
+ inner.adjustViewPosition()
+ if (targetPosition != null && currentPosition == null) {
+ header.isContentVisible = true
+ }
+ }
+ }
+ }
+
/**
* Should be called whenever notifs are added, removed, or updated. Updates section boundary
* bookkeeping and adds/moves/removes section headers if appropriate.
@@ -238,233 +291,136 @@
// Then, once we find the start of a new section, we track that position as the "target" for
// the section header, adjusted for the case where existing headers are in front of that
// target, but won't be once they are moved / removed after the pass has completed.
+
val showHeaders = statusBarStateController.state != StatusBarState.KEYGUARD
val usingPeopleFiltering = sectionsFeatureManager.isFilteringEnabled()
val usingMediaControls = sectionsFeatureManager.isMediaControlsEnabled()
+ val mediaState = mediaControlsView?.let(::expandableViewHeaderState)
+ val incomingState = incomingHeaderView?.let(::decorViewHeaderState)
+ val peopleState = peopleHeaderView?.let(::decorViewHeaderState)
+ val alertingState = alertingHeaderView?.let(::decorViewHeaderState)
+ val gentleState = silentHeaderView?.let(::decorViewHeaderState)
+
+ fun getSectionState(view: View): SectionUpdateState<ExpandableView>? = when {
+ view === mediaControlsView -> mediaState
+ view === incomingHeaderView -> incomingState
+ view === peopleHeaderView -> peopleState
+ view === alertingHeaderView -> alertingState
+ view === silentHeaderView -> gentleState
+ else -> null
+ }
+
+ val headersOrdered = sequenceOf(
+ mediaState, incomingState, peopleState, alertingState, gentleState
+ ).filterNotNull()
+
var peopleNotifsPresent = false
- var currentMediaControlsIdx = -1
- val mediaControlsTarget = if (usingMediaControls) 0 else -1
- var currentIncomingHeaderIdx = -1
- var incomingHeaderTarget = -1
- var currentPeopleHeaderIdx = -1
- var peopleHeaderTarget = -1
- var currentAlertingHeaderIdx = -1
- var alertingHeaderTarget = -1
- var currentGentleHeaderIdx = -1
- var gentleHeaderTarget = -1
-
var lastNotifIndex = 0
- var lastIncomingIndex = -1
- var prev: ExpandableNotificationRow? = null
+ var nextBucket: Int? = null
+ var inIncomingSection = false
- for ((i, child) in parent.children.withIndex()) {
- when {
- // Track the existing positions of the headers
- child === incomingHeaderView -> {
- logger.logIncomingHeader(i)
- currentIncomingHeaderIdx = i
- }
- child === mediaControlsView -> {
- logger.logMediaControls(i)
- currentMediaControlsIdx = i
- }
- child === peopleHeaderView -> {
- logger.logConversationsHeader(i)
- currentPeopleHeaderIdx = i
- }
- child === alertingHeaderView -> {
- logger.logAlertingHeader(i)
- currentAlertingHeaderIdx = i
- }
- child === silentHeaderView -> {
- logger.logSilentHeader(i)
- currentGentleHeaderIdx = i
- }
- child !is ExpandableNotificationRow -> logger.logOther(i, child.javaClass)
- else -> {
- lastNotifIndex = i
- // Is there a section discontinuity? This usually occurs due to HUNs
- if (prev?.entry?.bucket?.let { it > child.entry.bucket } == true) {
- // Remove existing headers, and move the Incoming header if necessary
- incomingHeaderTarget = when {
- !showHeaders -> -1
- incomingHeaderTarget != -1 -> incomingHeaderTarget
- peopleHeaderTarget != -1 -> peopleHeaderTarget
- alertingHeaderTarget != -1 -> alertingHeaderTarget
- gentleHeaderTarget != -1 -> gentleHeaderTarget
- else -> 0
- }
- peopleHeaderTarget = -1
- alertingHeaderTarget = -1
- gentleHeaderTarget = -1
- // Walk backwards changing all previous notifications to the Incoming
- // section
- for (j in i - 1 downTo lastIncomingIndex + 1) {
- val prevChild = parent.getChildAt(j)
- if (prevChild is ExpandableNotificationRow) {
- prevChild.entry.bucket = BUCKET_HEADS_UP
- }
- }
- // Track the new bottom of the Incoming section
- lastIncomingIndex = i - 1
- }
- val isHeadsUp = child.isHeadsUp
- when (child.entry.bucket) {
- BUCKET_FOREGROUND_SERVICE -> logger.logForegroundService(i, isHeadsUp)
- BUCKET_PEOPLE -> {
- logger.logConversation(i, isHeadsUp)
- peopleNotifsPresent = true
- if (showHeaders && peopleHeaderTarget == -1) {
- peopleHeaderTarget = i
- // Offset the target if there are other headers before this that
- // will be moved.
- if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
- peopleHeaderTarget--
- }
- if (currentPeopleHeaderIdx != -1) {
- peopleHeaderTarget--
- }
- if (currentAlertingHeaderIdx != -1) {
- peopleHeaderTarget--
- }
- if (currentGentleHeaderIdx != -1) {
- peopleHeaderTarget--
- }
- }
- }
- BUCKET_ALERTING -> {
- logger.logAlerting(i, isHeadsUp)
- if (showHeaders && usingPeopleFiltering && alertingHeaderTarget == -1) {
- alertingHeaderTarget = i
- // Offset the target if there are other headers before this that
- // will be moved.
- if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
- alertingHeaderTarget--
- }
- if (currentPeopleHeaderIdx != -1 && peopleHeaderTarget == -1) {
- // People header will be removed
- alertingHeaderTarget--
- }
- if (currentAlertingHeaderIdx != -1) {
- alertingHeaderTarget--
- }
- if (currentGentleHeaderIdx != -1) {
- alertingHeaderTarget--
- }
- }
- }
- BUCKET_SILENT -> {
- logger.logSilent(i, isHeadsUp)
- if (showHeaders && gentleHeaderTarget == -1) {
- gentleHeaderTarget = i
- // Offset the target if there are other headers before this that
- // will be moved.
- if (currentIncomingHeaderIdx != -1 && incomingHeaderTarget == -1) {
- gentleHeaderTarget--
- }
- if (currentPeopleHeaderIdx != -1 && peopleHeaderTarget == -1) {
- // People header will be removed
- gentleHeaderTarget--
- }
- if (currentAlertingHeaderIdx != -1 && alertingHeaderTarget == -1) {
- // Alerting header will be removed
- gentleHeaderTarget--
- }
- if (currentGentleHeaderIdx != -1) {
- gentleHeaderTarget--
- }
- }
- }
- }
-
- prev = child
+ // Iterating backwards allows for easier construction of the Incoming section, as opposed
+ // to backtracking when a discontinuity in the sections is discovered.
+ // Iterating to -1 in order to support the case where a header is at the very top of the
+ // shade.
+ for (i in parent.childCount - 1 downTo -1) {
+ val child: View? = parent.getChildAt(i)
+ child?.let {
+ logShadeChild(i, child)
+ // If this child is a header, update the tracked positions
+ getSectionState(child)?.let { state ->
+ state.currentPosition = i
+ // If headers that should appear above this one in the shade already have a
+ // target index, then we need to decrement them in order to account for this one
+ // being either removed, or moved below them.
+ headersOrdered.takeUntil { it === state }
+ .forEach { it.targetPosition = it.targetPosition?.minus(1) }
}
}
+
+ val row = child as? ExpandableNotificationRow
+
+ // Is there a section discontinuity? This usually occurs due to HUNs
+ inIncomingSection = inIncomingSection || nextBucket?.let { next ->
+ row?.entry?.bucket?.let { curr -> next < curr }
+ } == true
+
+ if (inIncomingSection) {
+ // Update the bucket to reflect that it's being placed in the Incoming section
+ row?.entry?.bucket = BUCKET_HEADS_UP
+ }
+
+ // Insert a header in front of the next row, if there's a boundary between it and this
+ // row, or if it is the topmost row.
+ val isSectionBoundary = nextBucket != null &&
+ (child == null || row != null && nextBucket != row.entry.bucket)
+ if (isSectionBoundary && showHeaders) {
+ when (nextBucket) {
+ BUCKET_HEADS_UP -> incomingState?.targetPosition = i + 1
+ BUCKET_PEOPLE -> peopleState?.targetPosition = i + 1
+ BUCKET_ALERTING -> alertingState?.targetPosition = i + 1
+ BUCKET_SILENT -> gentleState?.targetPosition = i + 1
+ }
+ }
+
+ row ?: continue
+
+ // Check if there are any people notifications
+ peopleNotifsPresent = peopleNotifsPresent || row.entry.bucket == BUCKET_PEOPLE
+
+ if (nextBucket == null) {
+ lastNotifIndex = i
+ }
+ nextBucket = row.entry.bucket
}
- if (showHeaders && usingPeopleFiltering && peopleHubVisible && peopleHeaderTarget == -1) {
- // Insert the people header even if there are no people visible, in order to show
- // the hub. Put it directly above the next header.
- peopleHeaderTarget = when {
- alertingHeaderTarget != -1 -> alertingHeaderTarget
- gentleHeaderTarget != -1 -> gentleHeaderTarget
- else -> lastNotifIndex // Put it at the end of the list.
- }
+ if (showHeaders && usingPeopleFiltering && peopleHubVisible) {
+ peopleState?.targetPosition = peopleState?.targetPosition
+ // Insert the people header even if there are no people visible, in order to
+ // show the hub. Put it directly above the next header.
+ ?: alertingState?.targetPosition
+ ?: gentleState?.targetPosition
+ // Put it at the end of the list.
+ ?: lastNotifIndex
+
// Offset the target to account for the current position of the people header.
- if (currentPeopleHeaderIdx != -1 && currentPeopleHeaderIdx < peopleHeaderTarget) {
- peopleHeaderTarget--
+ peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
+ peopleState?.targetPosition?.let { target ->
+ if (current < target) target - 1 else target
+ }
}
}
+ mediaState?.targetPosition = if (usingMediaControls) 0 else null
+
logger.logStr("New header target positions:")
- logger.logIncomingHeader(incomingHeaderTarget)
- logger.logMediaControls(mediaControlsTarget)
- logger.logConversationsHeader(peopleHeaderTarget)
- logger.logAlertingHeader(alertingHeaderTarget)
- logger.logSilentHeader(gentleHeaderTarget)
+ logger.logMediaControls(mediaState?.targetPosition ?: -1)
+ logger.logIncomingHeader(incomingState?.targetPosition ?: -1)
+ logger.logConversationsHeader(peopleState?.targetPosition ?: -1)
+ logger.logAlertingHeader(alertingState?.targetPosition ?: -1)
+ logger.logSilentHeader(gentleState?.targetPosition ?: -1)
- // Add headers in reverse order to preserve indices
- silentHeaderView?.let {
- adjustHeaderVisibilityAndPosition(gentleHeaderTarget, it, currentGentleHeaderIdx)
- }
- alertingHeaderView?.let {
- adjustHeaderVisibilityAndPosition(alertingHeaderTarget, it, currentAlertingHeaderIdx)
- }
- peopleHeaderView?.let {
- adjustHeaderVisibilityAndPosition(peopleHeaderTarget, it, currentPeopleHeaderIdx)
- }
- incomingHeaderView?.let {
- adjustHeaderVisibilityAndPosition(incomingHeaderTarget, it, currentIncomingHeaderIdx)
- }
- mediaControlsView?.let {
- adjustViewPosition(mediaControlsTarget, it, currentMediaControlsIdx)
- }
+ // Update headers in reverse order to preserve indices, otherwise movements earlier in the
+ // list will affect the target indices of the headers later in the list.
+ headersOrdered.asIterable().reversed().forEach { it.adjustViewPosition() }
logger.logStr("Final order:")
logShadeContents()
logger.logStr("Section boundary update complete")
// Update headers to reflect state of section contents
- silentHeaderView?.setAreThereDismissableGentleNotifs(
- parent.hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
- )
- peopleHeaderView?.canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
- if (peopleHeaderTarget != currentPeopleHeaderIdx) {
- peopleHeaderView?.resetTranslation()
+ silentHeaderView?.run {
+ val hasActiveClearableNotifications = this@NotificationSectionsManager.parent
+ .hasActiveClearableNotifications(NotificationStackScrollLayout.ROWS_GENTLE)
+ setAreThereDismissableGentleNotifs(hasActiveClearableNotifications)
}
- }
-
- private fun adjustHeaderVisibilityAndPosition(
- targetPosition: Int,
- header: StackScrollerDecorView,
- currentPosition: Int
- ) {
- adjustViewPosition(targetPosition, header, currentPosition)
- if (targetPosition != -1 && currentPosition == -1) {
- header.isContentVisible = true
- }
- }
-
- private fun adjustViewPosition(
- targetPosition: Int,
- view: ExpandableView,
- currentPosition: Int
- ) {
- if (targetPosition == -1) {
- if (currentPosition != -1) {
- parent.removeView(view)
- }
- } else {
- if (currentPosition == -1) {
- // If the header is animating away, it will still have a parent, so detach it first
- // TODO: We should really cancel the active animations here. This will happen
- // automatically when the view's intro animation starts, but it's a fragile link.
- view.transientContainer?.removeTransientView(view)
- view.transientContainer = null
- parent.addView(view, targetPosition)
- } else {
- parent.changeViewPosition(view, targetPosition)
+ peopleHeaderView?.run {
+ canSwipe = showHeaders && peopleHubVisible && !peopleNotifsPresent
+ peopleState?.targetPosition?.let { targetPosition ->
+ if (targetPosition != peopleState.currentPosition) {
+ resetTranslation()
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 825919f..fc8c8db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -19,7 +19,6 @@
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
-import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -35,7 +34,6 @@
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.util.UserAwareController;
import java.util.ArrayList;
import java.util.Objects;
@@ -45,7 +43,7 @@
/**
* Manages which tiles should be automatically added to QS.
*/
-public class AutoTileManager implements UserAwareController {
+public class AutoTileManager {
private static final String TAG = "AutoTileManager";
public static final String HOTSPOT = "hotspot";
@@ -54,9 +52,7 @@
public static final String WORK = "work";
public static final String NIGHT = "night";
public static final String CAST = "cast";
- static final String SETTING_SEPARATOR = ":";
-
- private UserHandle mCurrentUser;
+ public static final String SETTING_SEPARATOR = ":";
private final Context mContext;
private final QSTileHost mHost;
@@ -70,56 +66,43 @@
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@Inject
- public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
- QSTileHost host,
+ public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host,
@Background Handler handler,
HotspotController hotspotController,
DataSaverController dataSaverController,
ManagedProfileController managedProfileController,
NightDisplayListener nightDisplayListener,
CastController castController) {
+ mAutoTracker = autoAddTracker;
mContext = context;
mHost = host;
- mCurrentUser = mHost.getUserContext().getUser();
- mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
mHandler = handler;
mHotspotController = hotspotController;
mDataSaverController = dataSaverController;
mManagedProfileController = managedProfileController;
mNightDisplayListener = nightDisplayListener;
mCastController = castController;
-
- populateSettingsList();
- startControllersAndSettingsListeners();
- }
-
- protected void startControllersAndSettingsListeners() {
if (!mAutoTracker.isAdded(HOTSPOT)) {
- mHotspotController.addCallback(mHotspotCallback);
+ hotspotController.addCallback(mHotspotCallback);
}
if (!mAutoTracker.isAdded(SAVER)) {
- mDataSaverController.addCallback(mDataSaverListener);
+ dataSaverController.addCallback(mDataSaverListener);
}
if (!mAutoTracker.isAdded(WORK)) {
- mManagedProfileController.addCallback(mProfileCallback);
+ managedProfileController.addCallback(mProfileCallback);
}
if (!mAutoTracker.isAdded(NIGHT)
&& ColorDisplayManager.isNightDisplayAvailable(mContext)) {
- mNightDisplayListener.setCallback(mNightDisplayCallback);
+ nightDisplayListener.setCallback(mNightDisplayCallback);
}
if (!mAutoTracker.isAdded(CAST)) {
- mCastController.addCallback(mCastCallback);
+ castController.addCallback(mCastCallback);
}
-
- int settingsN = mAutoAddSettingList.size();
- for (int i = 0; i < settingsN; i++) {
- if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
- mAutoAddSettingList.get(i).setListening(true);
- }
- }
+ populateSettingsList();
}
- protected void stopListening() {
+ public void destroy() {
+ mAutoTracker.destroy();
mHotspotController.removeCallback(mHotspotCallback);
mDataSaverController.removeCallback(mDataSaverListener);
mManagedProfileController.removeCallback(mProfileCallback);
@@ -133,11 +116,6 @@
}
}
- public void destroy() {
- stopListening();
- mAutoTracker.destroy();
- }
-
/**
* Populates a list with the pairs setting:spec in the config resource.
* <p>
@@ -159,39 +137,17 @@
if (split.length == 2) {
String setting = split[0];
String spec = split[1];
- // Populate all the settings. As they may not have been added in other users
- AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
- mAutoAddSettingList.add(s);
+ if (!mAutoTracker.isAdded(spec)) {
+ AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec);
+ mAutoAddSettingList.add(s);
+ s.setListening(true);
+ }
} else {
Log.w(TAG, "Malformed item in array: " + tile);
}
}
}
- @Override
- public void changeUser(UserHandle newUser) {
- if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
- mHandler.post(() -> changeUser(newUser));
- return;
- }
- if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
- return;
- }
- stopListening();
- mCurrentUser = newUser;
- int settingsN = mAutoAddSettingList.size();
- for (int i = 0; i < settingsN; i++) {
- mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
- }
- mAutoTracker.changeUser(newUser);
- startControllersAndSettingsListeners();
- }
-
- @Override
- public int getCurrentUserId() {
- return mCurrentUser.getIdentifier();
- }
-
public void unmarkTileAsAutoAdded(String tabSpec) {
mAutoTracker.setTileRemoved(tabSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 11022bb..df121f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -323,6 +323,20 @@
buttonDispatcher.setAlpha(forceVisible ? 1f : alpha, animate);
}
}
+
+ @Override
+ public void onOverviewShown(boolean fromHome) {
+ // If the overview has fixed orientation that may change display to natural rotation,
+ // we don't want the user rotation to be reset. So after user returns to application,
+ // it can keep in the original rotation.
+ mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+ }
+
+ @Override
+ public void onToggleRecentApps() {
+ // The same case as onOverviewShown but only for 3-button navigation.
+ mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
+ }
};
private NavigationBarTransitions.DarkIntensityListener mOrientationHandleIntensityListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9e7bf62..e720d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1328,11 +1328,15 @@
*
* @param velocity unit is in px / millis
*/
- public void stopWaitingForOpenPanelGesture(final float velocity) {
+ public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) {
if (mExpectingSynthesizedDown) {
mExpectingSynthesizedDown = false;
- maybeVibrateOnOpening();
- fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
+ if (cancel) {
+ collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+ } else {
+ maybeVibrateOnOpening();
+ fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
+ }
onTrackingStopped(false);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
index de9c745..59b10e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java
@@ -74,6 +74,7 @@
private Consumer<Integer> mRotWatcherListener;
private boolean mListenersRegistered = false;
private boolean mIsNavigationBarShowing;
+ private boolean mSkipOverrideUserLockPrefsOnce;
private final Runnable mRemoveRotationProposal =
() -> setRotateSuggestionButtonState(false /* visible */);
@@ -349,7 +350,20 @@
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_SHOWN);
}
+ /**
+ * Makes {@link #shouldOverrideUserLockPrefs} always return {@code false} once. It is used to
+ * avoid losing original user rotation when display rotation is changed by entering the fixed
+ * orientation overview.
+ */
+ void setSkipOverrideUserLockPrefsOnce() {
+ mSkipOverrideUserLockPrefsOnce = true;
+ }
+
private boolean shouldOverrideUserLockPrefs(final int rotation) {
+ if (mSkipOverrideUserLockPrefsOnce) {
+ mSkipOverrideUserLockPrefsOnce = false;
+ return false;
+ }
// Only override user prefs when returning to the natural rotation (normally portrait).
// Don't let apps that force landscape or 180 alter user lock.
return rotation == NATURAL_ROTATION;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9300126..3772ffe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1772,6 +1772,9 @@
}
public void setPanelExpanded(boolean isExpanded) {
+ if (mPanelExpanded != isExpanded) {
+ mNotificationLogger.onPanelExpandedChanged(isExpanded);
+ }
mPanelExpanded = isExpanded;
updateHideIconsForBouncer(false /* animate */);
mNotificationShadeWindowController.setPanelExpanded(isExpanded);
@@ -2090,7 +2093,7 @@
/**
* Called when another window is about to transfer it's input focus.
*/
- public void onInputFocusTransfer(boolean start, float velocity) {
+ public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
if (!mCommandQueue.panelsEnabled()) {
return;
}
@@ -2098,7 +2101,7 @@
if (start) {
mNotificationPanelViewController.startWaitingForOpenPanelGesture();
} else {
- mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity);
+ mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
}
}
@@ -2878,7 +2881,6 @@
}
// Visibility reporting
-
protected void handleVisibleToUserChanged(boolean visibleToUser) {
if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser);
@@ -2900,12 +2902,12 @@
}
}
- /**
- * The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
- */
- private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
+ // Visibility reporting
+
+ void handleVisibleToUserChangedImpl(boolean visibleToUser) {
if (visibleToUser) {
+ /* The LEDs are turned off when the notification panel is shown, even just a little bit.
+ * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */
boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects =
!mPresenter.isPresenterFullyCollapsed() &&
@@ -3983,7 +3985,7 @@
} else if (mIsKeyguard && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else if (mBubbleController.isStackExpanded()) {
- mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
+ mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED, mUnlockScrimCallback);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index c91033e..631ea9d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -22,4 +22,14 @@
val ViewGroup.children
get() = sequence {
for (i in 0 until childCount) yield(getChildAt(i))
- }
\ No newline at end of file
+ }
+
+/** Inclusive version of [Iterable.takeWhile] */
+fun <T> Sequence<T>.takeUntil(pred: (T) -> Boolean): Sequence<T> = sequence {
+ for (x in this@takeUntil) {
+ yield(x)
+ if (pred(x)) {
+ break
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt b/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
index accb81e..1a25c84 100644
--- a/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
@@ -19,6 +19,22 @@
import android.util.SparseArray
/**
+ * Transforms a sequence of Key/Value pairs into a SparseArray.
+ *
+ * See [kotlin.collections.toMap].
+ */
+fun <T> Sequence<Pair<Int, T>>.toSparseArray(size: Int = -1): SparseArray<T> {
+ val sparseArray = when {
+ size < 0 -> SparseArray<T>()
+ else -> SparseArray<T>(size)
+ }
+ for ((i, v) in this) {
+ sparseArray.put(i, v)
+ }
+ return sparseArray
+}
+
+/**
* Transforms an [Array] into a [SparseArray], by applying each element to [keySelector] in order to
* generate the index at which it will be placed. If two elements produce the same index, the latter
* replaces the former in the final result.
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 701ff5e..806d9d8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -37,6 +37,7 @@
) : ConstraintLayout(context, attrs, defStyleAttr) {
private val originalGoneChildrenSet: MutableSet<Int> = mutableSetOf()
+ private val originalViewAlphas: MutableMap<Int, Float> = mutableMapOf()
private var measureAsConstraint: Boolean = false
private var currentState: TransitionViewState = TransitionViewState()
private var updateScheduled = false
@@ -67,6 +68,7 @@
if (child.visibility == GONE) {
originalGoneChildrenSet.add(child.id)
}
+ originalViewAlphas[child.id] = child.alpha
}
}
@@ -198,6 +200,8 @@
if (originalGoneChildrenSet.contains(child.id)) {
child.visibility = View.GONE
}
+ // Reset the alphas, to only have the alphas present from the constraintset
+ child.alpha = originalViewAlphas[child.id] ?: 1.0f
}
// Let's now apply the constraintSet to get the full state
constraintSet.applyTo(this)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index e324d84..a867825 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -18,7 +18,9 @@
import static android.view.WindowInsets.Type.ime;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -110,6 +112,7 @@
mKeyguardSecurityContainer.startDisappearAnimation(null);
verify(mSecurityView).startDisappearAnimation(eq(null));
- verify(mWindowInsetsController).hide(eq(ime()));
+ verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
+ any(), any());
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 36398a6..59f8c4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -304,10 +304,6 @@
public void testPromoteBubble_autoExpand() throws Exception {
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
- .thenReturn(mRow.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
- .thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
@@ -335,10 +331,6 @@
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
false, /* showInShade */ true);
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
- .thenReturn(mRow.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
- .thenReturn(mRow2.getEntry());
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
@@ -441,16 +433,15 @@
assertTrue(mSysUiStateBubblesExpanded);
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
+ assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
// Switch which bubble is expanded
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
- mRow.getEntry().getKey()));
+ mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
- assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -552,27 +543,27 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow2.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+ .getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
- assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
+ .getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -848,12 +839,6 @@
mRow2.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
mBubbleController.updateBubble(
mRow3.getEntry(), /* suppressFlyout */ false, /* showInShade */ false);
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
- .thenReturn(mRow.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getEntry().getKey()))
- .thenReturn(mRow2.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow3.getEntry().getKey()))
- .thenReturn(mRow3.getEntry());
assertEquals(mBubbleData.getBubbles().size(), 3);
mBubbleData.setMaxOverflowBubbles(1);
@@ -923,8 +908,6 @@
// GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -944,8 +927,6 @@
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -967,8 +948,6 @@
// GIVEN a group summary with two (non-bubble) children and one bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index be03923..72f816f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -86,7 +86,8 @@
final String msg = "Hello there!";
doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
- assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+ assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+ mEntry).message);
}
@Test
@@ -97,7 +98,8 @@
mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
// Should be big text, not the small text.
- assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+ assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mContext,
+ mEntry).message);
}
@Test
@@ -105,7 +107,8 @@
doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
// Media notifs don't get update messages.
- assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+ assertNull(BubbleViewInfoTask.extractFlyoutMessage(mContext,
+ mEntry).message);
}
@Test
@@ -121,7 +124,7 @@
// Should be the last one only.
assertEquals("Really? I prefer them that way.",
- BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+ BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
}
@Test
@@ -136,8 +139,11 @@
"Oh, hello!", 0, "Mady").toBundle()});
// Should be the last one only.
- assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
- assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
+ assertEquals("Oh, hello!",
+ BubbleViewInfoTask.extractFlyoutMessage(mContext, mEntry).message);
+ assertEquals("Mady",
+ BubbleViewInfoTask.extractFlyoutMessage(mContext,
+ mEntry).senderName);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 1c70db3..58e06b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -302,8 +302,6 @@
public void testRemoveBubble_withDismissedNotif_notInOverflow() {
mEntryListener.onEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getEntry().getKey()))
- .thenReturn(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
@@ -390,14 +388,14 @@
true, mRow.getEntry().getKey());
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry().getKey(), mBubbleData.getSelectedBubble().getKey());
+ assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
mBubbleData.setExpanded(true);
- assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -490,27 +488,27 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
// Last added is the one that is expanded
- assertEquals(mRow2.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow2.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry()));
// Dismiss currently expanded
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
+ stackView.getExpandedBubble().getKey()).getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
// Make sure first bubble is selected
- assertEquals(mRow.getEntry().getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
+ assertEquals(mRow.getEntry(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
// Dismiss that one
mBubbleController.removeBubble(
mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
+ stackView.getExpandedBubble().getKey()).getEntry().getKey(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -769,8 +767,6 @@
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -789,8 +785,6 @@
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -813,8 +807,6 @@
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
index 9b8fd11..1d02b8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -32,7 +32,7 @@
private val bubbles = listOf(
BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428),
BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
)
private lateinit var repository: BubblePersistentRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 76c5833..f9d611c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -37,10 +37,9 @@
private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10)
- private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0)
- private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
- "key-2", 0, 16537428, "title")
- private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+ private val bubble1 = BubbleEntity(0, PKG_MESSENGER, "shortcut-1", "k1", 120, 0)
+ private val bubble2 = BubbleEntity(10, PKG_CHAT, "alice and bob", "k2", 0, 16537428)
+ private val bubble3 = BubbleEntity(0, PKG_MESSENGER, "shortcut-2", "k3", 120, 0)
private val bubbles = listOf(bubble1, bubble2, bubble3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
index 81687c7..4946787 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/storage/BubbleXmlHelperTest.kt
@@ -31,17 +31,17 @@
class BubbleXmlHelperTest : SysuiTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
)
@Test
fun testWriteXml() {
val expectedEntries = """
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
""".trimIndent()
ByteArrayOutputStream().use {
writeXml(it, bubbles)
@@ -54,12 +54,12 @@
@Test
fun testReadXml() {
val src = """
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<bs>
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
-</bs>
+ <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+ <bs>
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+ <bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" />
+ <bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+ </bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertEquals("failed parsing bubbles from xml\n$src", bubbles, actual)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 329af2b..3c1cc23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -438,6 +438,9 @@
GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
assertThat(dialog).isNotNull();
assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.VISIBLE);
+
+ // Dismiss the dialog so that it does not pollute other tests
+ mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
}
@Test
@@ -455,6 +458,9 @@
GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
assertThat(dialog).isNotNull();
assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
+
+ // Dismiss the dialog so that it does not pollute other tests
+ mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
}
@Test
@@ -473,6 +479,9 @@
GlobalActionsDialog.ActionsDialog dialog = mGlobalActionsDialog.mDialog;
assertThat(dialog).isNotNull();
assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
+
+ // Dismiss the dialog so that it does not pollute other tests
+ mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
}
private void setupDefaultActions() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index 61f5a7b..0ae9461 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -41,8 +40,6 @@
@SmallTest
public class AutoAddTrackerTest extends SysuiTestCase {
- private static final int USER = 0;
-
private AutoAddTracker mAutoTracker;
@Before
@@ -54,7 +51,7 @@
public void testMigration() {
Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = new AutoAddTracker(mContext);
assertTrue(mAutoTracker.isAdded(SAVER));
assertTrue(mAutoTracker.isAdded(WORK));
@@ -71,7 +68,7 @@
@Test
public void testChangeFromBackup() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = new AutoAddTracker(mContext);
assertFalse(mAutoTracker.isAdded(SAVER));
@@ -85,7 +82,7 @@
@Test
public void testSetAdded() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = new AutoAddTracker(mContext);
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
@@ -97,35 +94,16 @@
@Test
public void testPersist() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = new AutoAddTracker(mContext);
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
mAutoTracker.destroy();
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = new AutoAddTracker(mContext);
assertTrue(mAutoTracker.isAdded(SAVER));
mAutoTracker.destroy();
}
-
- @Test
- public void testIndependentUsers() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.setTileAdded(SAVER);
-
- mAutoTracker = new AutoAddTracker(mContext, USER + 1);
- assertFalse(mAutoTracker.isAdded(SAVER));
- }
-
- @Test
- public void testChangeUser() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.setTileAdded(SAVER);
-
- mAutoTracker = new AutoAddTracker(mContext, USER + 1);
- mAutoTracker.changeUser(UserHandle.of(USER));
- assertTrue(mAutoTracker.isAdded(SAVER));
- }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index a3a46f6..06bad80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -42,6 +44,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -163,29 +166,62 @@
mUiBgExecutor.runAllReady();
Mockito.reset(mBarService);
- mLogger.stopNotificationLogging();
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
+ mLogger.onDozingChanged(true); // And go back to sleep, turning off logging
mUiBgExecutor.runAllReady();
// The visibility objects are recycled by NotificationLogger, so we can't use specific
// matchers here.
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
}
+ private void setStateAsleep() {
+ mLogger.onPanelExpandedChanged(true);
+ mLogger.onDozingChanged(true);
+ mLogger.onStateChanged(StatusBarState.KEYGUARD);
+ }
+
+ private void setStateAwake() {
+ mLogger.onPanelExpandedChanged(false);
+ mLogger.onDozingChanged(false);
+ mLogger.onStateChanged(StatusBarState.SHADE);
+ }
+
@Test
- public void testLogPanelShownOnLoggingStart() {
+ public void testLogPanelShownOnWake() {
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.startNotificationLogging();
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
- assertEquals(false, mNotificationPanelLoggerFake.get(0).isLockscreen);
+ assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
assertEquals(TEST_PACKAGE_NAME, n.packageName);
assertEquals(TEST_UID, n.uid);
assertEquals(1, n.instanceId);
- assertEquals(false, n.isGroupSummary);
+ assertFalse(n.isGroupSummary);
assertEquals(1 + BUCKET_ALERTING, n.section);
}
@Test
+ public void testLogPanelShownOnShadePull() {
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ setStateAwake();
+ // Now expand panel
+ mLogger.onPanelExpandedChanged(true);
+ assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+ assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
+ assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+ Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+ assertEquals(TEST_PACKAGE_NAME, n.packageName);
+ assertEquals(TEST_UID, n.uid);
+ assertEquals(1, n.instanceId);
+ assertFalse(n.isGroupSummary);
+ assertEquals(1 + BUCKET_ALERTING, n.section);
+ }
+
+
+ @Test
public void testLogPanelShownHandlesNullInstanceIds() {
// Construct a NotificationEntry like mEntry, but with a null instance id.
NotificationEntry entry = new NotificationEntryBuilder()
@@ -198,7 +234,8 @@
entry.setRow(mRow);
when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
- mLogger.startNotificationLogging();
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index c55391a..243503d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -403,11 +403,11 @@
enablePeopleFiltering();
setupMockStack(
- PERSON.headsUp(), // personHeaderTarget = 0
- INCOMING_HEADER, // currentIncomingHeaderIdx = 1
- ALERTING.headsUp(), // alertingHeaderTarget = 1
- PEOPLE_HEADER, // currentPeopleHeaderIdx = 3
- PERSON //
+ PERSON.headsUp(),
+ INCOMING_HEADER,
+ ALERTING.headsUp(),
+ PEOPLE_HEADER,
+ PERSON
);
mSectionsManager.updateSectionBoundaries();
@@ -520,6 +520,70 @@
ChildType.GENTLE);
}
+ @Test
+ public void testRemoveIncomingHeader() {
+ enablePeopleFiltering();
+ enableMediaControls();
+
+ setupMockStack(
+ MEDIA_CONTROLS,
+ INCOMING_HEADER,
+ PERSON,
+ ALERTING,
+ PEOPLE_HEADER,
+ ALERTING_HEADER,
+ ALERTING,
+ ALERTING,
+ GENTLE_HEADER,
+ GENTLE,
+ GENTLE
+ );
+
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.MEDIA_CONTROLS,
+ ChildType.PEOPLE_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING_HEADER,
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE,
+ ChildType.GENTLE
+ );
+ }
+
+ @Test
+ public void testExpandIncomingSection() {
+ enablePeopleFiltering();
+
+ setupMockStack(
+ INCOMING_HEADER,
+ PERSON,
+ ALERTING,
+ PEOPLE_HEADER,
+ ALERTING,
+ PERSON,
+ ALERTING_HEADER,
+ ALERTING
+ );
+
+ mSectionsManager.updateSectionBoundaries();
+
+ verifyMockStack(
+ ChildType.INCOMING_HEADER,
+ ChildType.HEADS_UP,
+ ChildType.HEADS_UP,
+ ChildType.HEADS_UP,
+ ChildType.PEOPLE_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING_HEADER,
+ ChildType.ALERTING
+ );
+ }
+
private void enablePeopleFiltering() {
when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
}
@@ -657,7 +721,13 @@
final List<View> children = new ArrayList<>();
when(mNssl.getChildCount()).thenAnswer(invocation -> children.size());
when(mNssl.getChildAt(anyInt()))
- .thenAnswer(invocation -> children.get(invocation.getArgument(0)));
+ .thenAnswer(invocation -> {
+ Integer index = invocation.getArgument(0);
+ if (index == null || index < 0 || index >= children.size()) {
+ return null;
+ }
+ return children.get(index);
+ });
when(mNssl.indexOfChild(any()))
.thenAnswer(invocation -> children.indexOf(invocation.getArgument(0)));
doAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 31779cd..05cdd80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -16,34 +16,18 @@
package com.android.systemui.statusbar.phone;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.isNotNull;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
-import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableContentResolver;
-import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -54,18 +38,14 @@
import com.android.systemui.qs.AutoAddTracker;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -84,18 +64,9 @@
private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
- private static final int USER = 0;
-
@Mock private QSTileHost mQsTileHost;
@Mock private AutoAddTracker mAutoAddTracker;
@Mock private CastController mCastController;
- @Mock private HotspotController mHotspotController;
- @Mock private DataSaverController mDataSaverController;
- @Mock private ManagedProfileController mManagedProfileController;
- @Mock private NightDisplayListener mNightDisplayListener;
- @Mock(answer = Answers.RETURNS_SELF)
- private AutoAddTracker.Builder mAutoAddTrackerBuilder;
- @Mock private Context mUserContext;
private AutoTileManager mAutoTileManager;
@@ -111,110 +82,20 @@
}
);
- when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
- when(mQsTileHost.getUserContext()).thenReturn(mUserContext);
- when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
-
- mAutoTileManager = createAutoTileManager(new
- MyContextWrapper(mContext));
+ mAutoTileManager = createAutoTileManager();
}
- @After
- public void tearDown() {
- mAutoTileManager.destroy();
- }
-
- private AutoTileManager createAutoTileManager(Context context) {
- return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost,
+ private AutoTileManager createAutoTileManager() {
+ return new AutoTileManager(mContext, mAutoAddTracker, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()),
- mHotspotController,
- mDataSaverController,
- mManagedProfileController,
- mNightDisplayListener,
+ mock(HotspotController.class),
+ mock(DataSaverController.class),
+ mock(ManagedProfileController.class),
+ mock(NightDisplayListener.class),
mCastController);
}
@Test
- public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 1))
- );
-
- InOrder inOrderHotspot = inOrder(mHotspotController);
- inOrderHotspot.verify(mHotspotController).removeCallback(any());
- inOrderHotspot.verify(mHotspotController).addCallback(any());
-
- InOrder inOrderDataSaver = inOrder(mDataSaverController);
- inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
- inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
- InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
- inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
- inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
- InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-
- InOrder inOrderCast = inOrder(mCastController);
- inOrderCast.verify(mCastController).removeCallback(any());
- inOrderCast.verify(mCastController).addCallback(any());
-
- SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
- assertEquals(USER + 1, setting.getCurrentUser());
- assertTrue(setting.isListening());
- }
-
- @Test
- public void testChangeUserSomeCallbacksNotAdded() throws Exception {
- when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
- when(mAutoAddTracker.isAdded("work")).thenReturn(true);
- when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
- when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 1))
- );
-
- verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
-
- InOrder inOrderHotspot = inOrder(mHotspotController);
- inOrderHotspot.verify(mHotspotController).removeCallback(any());
- inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
-
- InOrder inOrderDataSaver = inOrder(mDataSaverController);
- inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
- inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
- InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
- inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
- inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any());
-
- InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
- inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-
- InOrder inOrderCast = inOrder(mCastController);
- inOrderCast.verify(mCastController).removeCallback(any());
- inOrderCast.verify(mCastController, never()).addCallback(any());
-
- SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
- assertEquals(USER + 1, setting.getCurrentUser());
- assertFalse(setting.isListening());
- }
-
- @Test
- public void testGetCurrentUserId() throws Exception {
- assertEquals(USER, mAutoTileManager.getCurrentUserId());
-
- TestableLooper.get(this).runWithLooper(() ->
- mAutoTileManager.changeUser(UserHandle.of(USER + 100))
- );
-
- assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
- }
-
- @Test
public void nightTileAdded_whenActivated() {
if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
return;
@@ -332,14 +213,14 @@
public void testEmptyArray_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, new String[0]);
- createAutoTileManager(mContext).destroy();
+ createAutoTileManager();
}
@Test
public void testMissingConfig_doesNotCrash() {
mContext.getOrCreateTestableResources().addOverride(
R.array.config_quickSettingsAutoAdd, null);
- createAutoTileManager(mContext).destroy();
+ createAutoTileManager();
}
// Will only notify if it's listening
@@ -350,22 +231,4 @@
s.onChange(false);
}
}
-
- class MyContextWrapper extends ContextWrapper {
-
- private TestableContentResolver mSpiedTCR;
-
- MyContextWrapper(TestableContext context) {
- super(context);
- mSpiedTCR = spy(context.getContentResolver());
- doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(),
- anyInt());
- doNothing().when(mSpiedTCR).unregisterContentObserver(any());
- }
-
- @Override
- public ContentResolver getContentResolver() {
- return mSpiedTCR;
- }
- }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6ccdf24..961dc15 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -22,10 +22,14 @@
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkState;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
import android.annotation.CheckResult;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.companion.Association;
import android.companion.AssociationRequest;
@@ -36,6 +40,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
@@ -55,6 +60,7 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
@@ -71,6 +77,7 @@
import com.android.internal.infra.PerUser;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -112,6 +119,9 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "CompanionDeviceManagerService";
+ private static final String PREF_FILE_NAME = "companion_device_preferences.xml";
+ private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done";
+
private static final String XML_TAG_ASSOCIATIONS = "associations";
private static final String XML_TAG_ASSOCIATION = "association";
private static final String XML_ATTR_PACKAGE = "package";
@@ -150,7 +160,6 @@
}
};
-
registerPackageMonitor();
}
@@ -195,6 +204,36 @@
if (atmInternal != null) {
atmInternal.setCompanionAppPackages(userHandle, companionAppPackages);
}
+
+ BackgroundThread.getHandler().sendMessageDelayed(
+ obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this),
+ MINUTES.toMillis(10));
+ }
+
+ void maybeGrantAutoRevokeExemptions() {
+ PackageManager pm = getContext().getPackageManager();
+ for (int userId : LocalServices.getService(UserManagerInternal.class).getUserIds()) {
+ SharedPreferences pref = getContext().getSharedPreferences(
+ new File(Environment.getUserSystemDirectory(userId), PREF_FILE_NAME),
+ Context.MODE_PRIVATE);
+ if (pref.getBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, false)) {
+ continue;
+ }
+
+ try {
+ Set<Association> associations = readAllAssociations(userId);
+ for (Association a : associations) {
+ try {
+ int uid = pm.getPackageUidAsUser(a.companionAppPackage, userId);
+ exemptFromAutoRevoke(a.companionAppPackage, uid);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Unknown companion package: " + a.companionAppPackage, e);
+ }
+ }
+ } finally {
+ pref.edit().putBoolean(PREF_KEY_AUTO_REVOKE_GRANTS_DONE, true).apply();
+ }
+ }
}
@Override
@@ -469,6 +508,21 @@
packageInfo.applicationInfo.uid,
NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
}
+
+ exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
+ }
+
+ private void exemptFromAutoRevoke(String packageName, int uid) {
+ try {
+ mAppOpsManager.setMode(
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+ uid,
+ packageName,
+ AppOpsManager.MODE_IGNORED);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG,
+ "Error while granting auto revoke exemption for " + packageName, e);
+ }
}
private static <T> boolean containsEither(T[] array, T a, T b) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a3c164d..23bf955 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -310,27 +310,30 @@
private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates =
new ArrayList<Map<Integer, PreciseDataConnectionState>>();
- // Nothing here yet, but putting it here in case we want to add more in the future.
- static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+ static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+ PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+ | PhoneStateListener.LISTEN_BARRING_INFO;
static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
PhoneStateListener.LISTEN_CELL_LOCATION
- | PhoneStateListener.LISTEN_CELL_INFO;
+ | PhoneStateListener.LISTEN_CELL_INFO
+ | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+ | PhoneStateListener.LISTEN_BARRING_INFO;
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
- | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
- | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
+ PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+ | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
- PhoneStateListener.LISTEN_PRECISE_CALL_STATE
- | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
- | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
- | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
- | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
- | PhoneStateListener.LISTEN_BARRING_INFO;
+ PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+ | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+ | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+ | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
+ | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+ | PhoneStateListener.LISTEN_BARRING_INFO;
static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -338,9 +341,9 @@
static final int READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT
- | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
- | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
- | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
+ | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED
+ | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED
+ | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE;
private static final int MSG_USER_SWITCHED = 1;
private static final int MSG_UPDATE_DEFAULT_SUB = 2;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 35936ba..be080e5 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -233,7 +233,7 @@
public void onTwilightStateChanged(@Nullable TwilightState state) {
synchronized (mLock) {
if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
- if (mCar) {
+ if (shouldApplyAutomaticChangesImmediately()) {
updateLocked(0, 0);
} else {
registerScreenOffEventLocked();
@@ -1155,7 +1155,6 @@
void updateLocked(int enableFlags, int disableFlags) {
String action = null;
String oldAction = null;
- boolean originalComputedNightMode = mComputedNightMode;
if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) {
adjustStatusBarCarModeLocked();
oldAction = UiModeManager.ACTION_EXIT_CAR_MODE;
@@ -1236,16 +1235,11 @@
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
- // reset overrides if mComputedNightMode changes
- if (originalComputedNightMode != mComputedNightMode) {
- resetNightModeOverrideLocked();
- }
-
// keep screen on when charging and in car mode
boolean keepScreenOn = mCharging &&
((mCarModeEnabled && mCarModeKeepsScreenOn &&
- (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
- (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
+ (mCarModeEnableFlags & UiModeManager.ENABLE_CAR_MODE_ALLOW_SLEEP) == 0) ||
+ (mCurUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskModeKeepsScreenOn));
if (keepScreenOn != mWakeLock.isHeld()) {
if (keepScreenOn) {
mWakeLock.acquire();
@@ -1403,6 +1397,7 @@
mComputedNightMode = false;
return;
}
+ resetNightModeOverrideLocked();
}
private boolean resetNightModeOverrideLocked() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9a2aee3..93a27f2 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -598,6 +598,10 @@
UserRecord userRecord = routerRecord.mUserRecord;
userRecord.mRouterRecords.remove(routerRecord);
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
+ routerRecord.mUserRecord.mHandler,
+ routerRecord.mPackageName, /* preferredFeatures=*/ null));
userRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
userRecord.mHandler));
@@ -613,7 +617,9 @@
routerRecord.mDiscoveryPreference = discoveryRequest;
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
- routerRecord.mUserRecord.mHandler, routerRecord));
+ routerRecord.mUserRecord.mHandler,
+ routerRecord.mPackageName,
+ routerRecord.mDiscoveryPreference.getPreferredFeatures()));
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
routerRecord.mUserRecord.mHandler));
@@ -1954,7 +1960,8 @@
}
}
- private void notifyPreferredFeaturesChangedToManagers(@NonNull RouterRecord routerRecord) {
+ private void notifyPreferredFeaturesChangedToManagers(@NonNull String routerPackageName,
+ @Nullable List<String> preferredFeatures) {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
return;
@@ -1967,8 +1974,7 @@
}
for (IMediaRouter2Manager manager : managers) {
try {
- manager.notifyPreferredFeaturesChanged(routerRecord.mPackageName,
- routerRecord.mDiscoveryPreference.getPreferredFeatures());
+ manager.notifyPreferredFeaturesChanged(routerPackageName, preferredFeatures);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify preferred features changed."
+ " Manager probably died.", ex);
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java
index 3829b65..cfcf6eb 100644
--- a/services/core/java/com/android/server/notification/CalendarTracker.java
+++ b/services/core/java/com/android/server/notification/CalendarTracker.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
@@ -102,6 +103,8 @@
while (cursor != null && cursor.moveToNext()) {
rt.add(cursor.getLong(0));
}
+ } catch (SQLiteException e) {
+ Slog.w(TAG, "error querying calendar content provider", e);
} finally {
if (cursor != null) {
cursor.close();
@@ -118,11 +121,12 @@
ContentUris.appendId(uriBuilder, time);
ContentUris.appendId(uriBuilder, time + EVENT_CHECK_LOOKAHEAD);
final Uri uri = uriBuilder.build();
- final Cursor cursor = mUserContext.getContentResolver().query(uri, INSTANCE_PROJECTION,
- null, null, INSTANCE_ORDER_BY);
+ Cursor cursor = null;
final CheckEventResult result = new CheckEventResult();
result.recheckAt = time + EVENT_CHECK_LOOKAHEAD;
try {
+ cursor = mUserContext.getContentResolver().query(uri, INSTANCE_PROJECTION,
+ null, null, INSTANCE_ORDER_BY);
final ArraySet<Long> calendars = getCalendarsWithAccess();
while (cursor != null && cursor.moveToNext()) {
final long begin = cursor.getLong(0);
@@ -183,9 +187,10 @@
selection = null;
selectionArgs = null;
}
- final Cursor cursor = mUserContext.getContentResolver().query(Attendees.CONTENT_URI,
- ATTENDEE_PROJECTION, selection, selectionArgs, null);
+ Cursor cursor = null;
try {
+ cursor = mUserContext.getContentResolver().query(Attendees.CONTENT_URI,
+ ATTENDEE_PROJECTION, selection, selectionArgs, null);
if (cursor == null || cursor.getCount() == 0) {
if (DEBUG) Log.d(TAG, "No attendees found");
return true;
@@ -205,6 +210,9 @@
rt |= eventMeets;
}
return rt;
+ } catch (SQLiteException e) {
+ Slog.w(TAG, "error querying attendees content provider", e);
+ return false;
} finally {
if (cursor != null) {
cursor.close();
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 0bd8b28..fc58968 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -133,8 +133,10 @@
@Override
public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
File appLib32InstallDir) {
- return getNativeLibraryPaths(new Abis(pkg, pkgSetting), appLib32InstallDir,
- pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+ // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
+ // current state in PackageSetting is irrelevant.
+ return getNativeLibraryPaths(new Abis(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi()),
+ appLib32InstallDir, pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
pkgSetting.getPkgState().isUpdatedSystemApp());
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d3d9bc7..f5a49d2 100755
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2128,6 +2128,7 @@
baseApk = apk;
}
+ // Validate and add Dex Metadata (.dm).
final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(addedFile);
if (dexMetadataFile != null) {
if (!FileUtils.isValidExtFilename(dexMetadataFile.getName())) {
@@ -2315,6 +2316,13 @@
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
+
+ final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
+ if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
+ if (!baseApk.debuggable && !baseApk.profilableByShell) {
+ mIncrementalFileStorages.disableReadLogs();
+ }
+ }
}
private void resolveAndStageFile(File origFile, File targetFile)
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8ccf837..515225b 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -53,6 +53,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.storage.IStorageManager;
@@ -1155,6 +1156,11 @@
}
private void checkStateAndResume(@NonNull PackageInstallerSession session) {
+ // Do not resume session if boot completed already
+ if (SystemProperties.getBoolean("sys.boot_completed", false)) {
+ return;
+ }
+
if (!session.isCommitted()) {
// Session hasn't been committed yet, ignore.
return;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 41aa4ee..7cb59dc 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -21,6 +21,7 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
+import android.media.IResourceManagerService;
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
@@ -53,7 +54,7 @@
*
* @hide
*/
-public class TunerResourceManagerService extends SystemService {
+public class TunerResourceManagerService extends SystemService implements IBinder.DeathRecipient {
private static final String TAG = "TunerResourceManagerService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -76,6 +77,7 @@
private TvInputManager mTvInputManager;
private ActivityManager mActivityManager;
+ private IResourceManagerService mMediaResourceManager;
private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
// An internal resource request count to help generate resource handle.
@@ -102,6 +104,22 @@
mActivityManager =
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
mPriorityCongfig.parse();
+
+ if (mMediaResourceManager == null) {
+ IBinder mediaResourceManagerBinder = getBinderService("media.resource_manager");
+ if (mediaResourceManagerBinder == null) {
+ Slog.w(TAG, "Resource Manager Service not available.");
+ return;
+ }
+ try {
+ mediaResourceManagerBinder.linkToDeath(this, /*flags*/ 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not link to death of native resource manager service.");
+ return;
+ }
+ mMediaResourceManager = IResourceManagerService.Stub.asInterface(
+ mediaResourceManagerBinder);
+ }
}
private final class BinderService extends ITunerResourceManager.Stub {
@@ -380,6 +398,19 @@
}
}
+ /**
+ * Handle the death of the native resource manager service
+ */
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.w(TAG, "Native media resource manager service has died");
+ }
+ synchronized (mLock) {
+ mMediaResourceManager = null;
+ }
+ }
+
@VisibleForTesting
protected void registerClientProfileInternal(ResourceClientProfile profile,
IResourcesReclaimListener listener, int[] clientId) {
@@ -399,6 +430,16 @@
? Binder.getCallingPid() /*callingPid*/
: mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+ // Update Media Resource Manager with the tvAppId
+ if (profile.getTvInputSessionId() != null && mMediaResourceManager != null) {
+ try {
+ mMediaResourceManager.overridePid(Binder.getCallingPid(), pid);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not overridePid in resourceManagerSercice,"
+ + " remote exception: " + e);
+ }
+ }
+
ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
.tvInputSessionId(profile.getTvInputSessionId())
.useCase(profile.getUseCase())
@@ -415,6 +456,15 @@
Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
}
removeClientProfile(clientId);
+ // Remove the Media Resource Manager callingPid to tvAppId mapping
+ if (mMediaResourceManager != null) {
+ try {
+ mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
+ + " remote exception: " + e);
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a072175..74ea712 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2207,10 +2207,13 @@
@Override
boolean isFocusable() {
+ return super.isFocusable() && (canReceiveKeys() || isAlwaysFocusable());
+ }
+
+ boolean canReceiveKeys() {
// TODO(156521483): Propagate the state down the hierarchy instead of checking the parent
- boolean canReceiveKeys = getWindowConfiguration().canReceiveKeys()
- && getTask().getWindowConfiguration().canReceiveKeys();
- return super.isFocusable() && (canReceiveKeys || isAlwaysFocusable());
+ return getWindowConfiguration().canReceiveKeys()
+ && (task == null || task.getWindowConfiguration().canReceiveKeys());
}
boolean isResizeable() {
@@ -2379,10 +2382,10 @@
// For the apps below Q, there can be only one app which has the focused window per
// process, because legacy apps may not be ready for a multi-focus system.
return false;
+
}
}
- return (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable())
- && getDisplay() != null;
+ return (canReceiveKeys() || isAlwaysFocusable()) && getDisplay() != null;
}
/**
@@ -3364,6 +3367,13 @@
final long origId = Binder.clearCallingIdentity();
try {
+ // Link the fixed rotation transform to this activity since we are transferring the
+ // starting window.
+ if (fromActivity.hasFixedRotationTransform()) {
+ mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(this,
+ false /* checkOpening */);
+ }
+
// Transfer the starting window over to the new token.
mStartingData = fromActivity.mStartingData;
startingSurface = fromActivity.startingSurface;
@@ -7744,24 +7754,25 @@
outAppBounds.set(outBounds);
}
} else {
- outBounds.set(0, 0, mWidth, mHeight);
- getFrameByOrientation(outAppBounds, orientation);
- if (orientationRequested && !canChangeOrientation
- && (outAppBounds.width() > outAppBounds.height()) != (mWidth > mHeight)) {
- // The orientation is mismatched but the display cannot rotate. The bounds will
- // fit to the short side of display.
- if (orientation == ORIENTATION_LANDSCAPE) {
- outAppBounds.bottom = (int) ((float) mWidth * mWidth / mHeight);
- outAppBounds.right = mWidth;
- } else {
- outAppBounds.bottom = mHeight;
- outAppBounds.right = (int) ((float) mHeight * mHeight / mWidth);
+ if (orientationRequested) {
+ getFrameByOrientation(outBounds, orientation);
+ if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) {
+ // The orientation is mismatched but the display cannot rotate. The bounds
+ // will fit to the short side of display.
+ if (orientation == ORIENTATION_LANDSCAPE) {
+ outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight);
+ outBounds.right = mWidth;
+ } else {
+ outBounds.bottom = mHeight;
+ outBounds.right = (int) ((float) mHeight * mHeight / mWidth);
+ }
+ outBounds.offset(
+ getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
}
- outAppBounds.offset(getHorizontalCenterOffset(outBounds.width(),
- outAppBounds.width()), 0 /* dy */);
} else {
- outAppBounds.set(outBounds);
+ outBounds.set(0, 0, mWidth, mHeight);
}
+ outAppBounds.set(outBounds);
}
if (rotation != ROTATION_UNDEFINED) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 7fd2156..34e8169 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -821,7 +821,7 @@
/** Resume next focusable stack after reparenting to another display. */
void postReparent() {
adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */,
- true /* moveParentsToTop */);
+ true /* moveDisplayToTop */);
mRootWindowContainer.resumeFocusedStacksTopActivities();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 8b14095..26e0790 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -59,6 +59,7 @@
private final int mTransparentFlag;
private final int mStatusBarManagerId;
private final int mTranslucentWmFlag;
+ private final int mWindowType;
protected final Handler mHandler;
private final Object mServiceAquireLock = new Object();
private StatusBarManagerInternal mStatusBarInternal;
@@ -77,13 +78,14 @@
private OnBarVisibilityChangedListener mVisibilityChangeListener;
BarController(String tag, int displayId, int transientFlag, int unhideFlag, int translucentFlag,
- int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
+ int statusBarManagerId, int windowType, int translucentWmFlag, int transparentFlag) {
mTag = "BarController." + tag;
mDisplayId = displayId;
mTransientFlag = transientFlag;
mUnhideFlag = unhideFlag;
mTranslucentFlag = translucentFlag;
mStatusBarManagerId = statusBarManagerId;
+ mWindowType = windowType;
mTranslucentWmFlag = translucentWmFlag;
mTransparentFlag = transparentFlag;
mHandler = new BarHandler();
@@ -168,7 +170,12 @@
}
boolean isTransparentAllowed(WindowState win) {
- return win == null || win.letterboxNotIntersectsOrFullyContains(mContentFrame);
+ if (win == null) {
+ return true;
+ }
+ final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
+ final Rect contentFrame = rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
+ return win.letterboxNotIntersectsOrFullyContains(contentFrame);
}
boolean setBarShowingLw(final boolean show) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4e19a52..fb60257 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5647,8 +5647,12 @@
}
if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) {
- // Because it won't affect display orientation, just finish the transform.
- animatingRecents.finishFixedRotationTransform();
+ // The recents activity should be going to be invisible (switch to another app or
+ // return to original top). Only clear the top launching record without finishing
+ // the transform immediately because it won't affect display orientation. And before
+ // the visibility is committed, the recents activity may perform relayout which may
+ // cause unexpected configuration change if the rotated configuration is restored.
+ // The transform will be finished when the transition is done.
setFixedRotationLaunchingAppUnchecked(null);
} else {
// If there is already a launching activity that is not the recents, before its
@@ -5671,7 +5675,11 @@
}
if (mFixedRotationLaunchingApp != null
&& mFixedRotationLaunchingApp.hasFixedRotationTransform(r)) {
- continueUpdateOrientationForDiffOrienLaunchingApp();
+ // Waiting until all of the associated activities have done animation, or the
+ // orientation would be updated too early and cause flickers.
+ if (!mFixedRotationLaunchingApp.hasAnimatingFixedRotationTransition()) {
+ continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
} else {
r.finishFixedRotationTransform();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 0838d79..01680b2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -156,6 +156,7 @@
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.InputChannel;
@@ -204,6 +205,7 @@
import com.android.server.wm.utils.InsetUtils;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* The policy that provides the basic behaviors and states of a display to show UI.
@@ -520,6 +522,7 @@
View.NAVIGATION_BAR_UNHIDE,
View.NAVIGATION_BAR_TRANSLUCENT,
StatusBarManager.WINDOW_NAVIGATION_BAR,
+ TYPE_NAVIGATION_BAR,
FLAG_TRANSLUCENT_NAVIGATION,
View.NAVIGATION_BAR_TRANSPARENT);
@@ -1317,6 +1320,11 @@
displayFrames.mDisplayCutoutSafe.top);
}
+ @VisibleForTesting
+ StatusBarController getStatusBarController() {
+ return mStatusBarController;
+ }
+
WindowState getStatusBar() {
return mStatusBar;
}
@@ -1615,13 +1623,16 @@
}
private void simulateLayoutDecorWindow(WindowState win, DisplayFrames displayFrames,
- InsetsState insetsState, WindowFrames simulatedWindowFrames, Runnable layout) {
+ InsetsState insetsState, WindowFrames simulatedWindowFrames,
+ SparseArray<Rect> contentFrames, Consumer<Rect> layout) {
win.setSimulatedWindowFrames(simulatedWindowFrames);
+ final Rect contentFrame = new Rect();
try {
- layout.run();
+ layout.accept(contentFrame);
} finally {
win.setSimulatedWindowFrames(null);
}
+ contentFrames.put(win.mAttrs.type, contentFrame);
mDisplayContent.getInsetsStateController().computeSimulatedState(insetsState, win,
displayFrames, simulatedWindowFrames);
}
@@ -1633,24 +1644,25 @@
* state and some temporal states. In other words, it doesn't change the window frames used to
* show on screen.
*/
- void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState, int uiMode) {
+ void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState,
+ SparseArray<Rect> barContentFrames) {
displayFrames.onBeginLayout();
updateInsetsStateForDisplayCutout(displayFrames, insetsState);
insetsState.setDisplayFrame(displayFrames.mUnrestricted);
final WindowFrames simulatedWindowFrames = new WindowFrames();
if (mNavigationBar != null) {
- simulateLayoutDecorWindow(
- mNavigationBar, displayFrames, insetsState, simulatedWindowFrames,
- () -> layoutNavigationBar(displayFrames, uiMode, mLastNavVisible,
+ simulateLayoutDecorWindow(mNavigationBar, displayFrames, insetsState,
+ simulatedWindowFrames, barContentFrames,
+ contentFrame -> layoutNavigationBar(displayFrames,
+ mDisplayContent.getConfiguration().uiMode, mLastNavVisible,
mLastNavTranslucent, mLastNavAllowedHidden,
- mLastNotificationShadeForcesShowingNavigation,
- false /* isRealLayout */));
+ mLastNotificationShadeForcesShowingNavigation, contentFrame));
}
if (mStatusBar != null) {
- simulateLayoutDecorWindow(
- mStatusBar, displayFrames, insetsState, simulatedWindowFrames,
- () -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
- false /* isRealLayout */));
+ simulateLayoutDecorWindow(mStatusBar, displayFrames, insetsState,
+ simulatedWindowFrames, barContentFrames,
+ contentFrame -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
+ contentFrame));
}
layoutScreenDecorWindows(displayFrames, simulatedWindowFrames);
postAdjustDisplayFrames(displayFrames);
@@ -1702,9 +1714,10 @@
boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
navTranslucent, navAllowedHidden, notificationShadeForcesShowingNavigation,
- true /* isRealLayout */);
+ null /* simulatedContentFrame */);
if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui, true /* isRealLayout */);
+ updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui,
+ null /* simulatedContentFrame */);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
@@ -1725,10 +1738,9 @@
navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null;
final InsetsState requestedState = navControllingWin != null
? navControllingWin.getRequestedInsetsState() : null;
- final InsetsSource navSource = requestedState != null
- ? requestedState.peekSource(ITYPE_NAVIGATION_BAR) : null;
- final boolean navVisible = navSource != null
- ? navSource.isVisible() : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
+ final boolean navVisible = requestedState != null
+ ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+ : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
final boolean showBarsByTouch = navControllingWin != null
&& navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH;
// When the navigation bar isn't visible, we put up a fake input window to catch all
@@ -1877,7 +1889,8 @@
displayFrames.mContent.set(dockFrame);
}
- private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui, boolean isRealLayout) {
+ private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ Rect simulatedContentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
return false;
@@ -1900,12 +1913,14 @@
displayFrames.mStable.top = Math.max(displayFrames.mStable.top,
displayFrames.mDisplayCutoutSafe.top);
- if (isRealLayout) {
- // Tell the bar controller where the collapsed status bar content is.
- sTmpRect.set(windowFrames.mContentFrame);
- sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
- sTmpRect.top = windowFrames.mContentFrame.top; // Ignore top display cutout inset
- sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ // Tell the bar controller where the collapsed status bar content is.
+ sTmpRect.set(windowFrames.mContentFrame);
+ sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ sTmpRect.top = windowFrames.mContentFrame.top; // Ignore top display cutout inset
+ sTmpRect.bottom = displayFrames.mStable.top; // Use collapsed status bar size
+ if (simulatedContentFrame != null) {
+ simulatedContentFrame.set(sTmpRect);
+ } else {
mStatusBarController.setContentFrame(sTmpRect);
}
@@ -1942,7 +1957,7 @@
private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
boolean navTranslucent, boolean navAllowedHidden,
- boolean statusBarForcesShowingNavigation, boolean isRealLayout) {
+ boolean statusBarForcesShowingNavigation, Rect simulatedContentFrame) {
if (mNavigationBar == null) {
return false;
}
@@ -2046,7 +2061,9 @@
navigationFrame /* visibleFrame */, sTmpRect /* decorFrame */,
navigationFrame /* stableFrame */);
mNavigationBar.computeFrame(displayFrames);
- if (isRealLayout) {
+ if (simulatedContentFrame != null) {
+ simulatedContentFrame.set(windowFrames.mContentFrame);
+ } else {
mNavigationBarPosition = navBarPosition;
mNavigationBarController.setContentFrame(windowFrames.mContentFrame);
}
@@ -2518,12 +2535,13 @@
final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
|| (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
- && !win.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+ && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+ ITYPE_STATUS_BAR));
final boolean requestedHideNavigation =
(requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
- && !win.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
- .isVisible());
+ && !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
+ ITYPE_NAVIGATION_BAR));
// TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
// cropped / shifted to the displayFrame in WindowState.
@@ -3333,24 +3351,32 @@
return;
}
if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- if (swipeTarget == mNavigationBar
- && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
- // Don't show status bar when swiping on already visible navigation bar
- return;
- }
final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider();
final InsetsControlTarget controlTarget = provider != null
? provider.getControlTarget() : null;
- // No transient mode on lockscreen (in notification shade window).
if (controlTarget == null || controlTarget == getNotificationShade()) {
+ // No transient mode on lockscreen (in notification shade window).
return;
}
+
+ if (swipeTarget == mNavigationBar
+ && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) {
+ // Don't show status bar when swiping on already visible navigation bar.
+ // But restore the position of navigation bar if it has been moved by the control
+ // target.
+ controlTarget.showInsets(Type.navigationBars(), false);
+ return;
+ }
+
+ int insetsTypesToShow = Type.systemBars();
+
if (controlTarget.canShowTransient()) {
- mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
+ insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap(
new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
- } else {
- controlTarget.showInsets(Type.systemBars(), false);
+ }
+ if (insetsTypesToShow != 0) {
+ controlTarget.showInsets(insetsTypesToShow, false);
}
} else {
boolean sb = mStatusBarController.checkShowTransientBarLw();
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 035f201..be6e4b7 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -42,6 +42,7 @@
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
+import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
@@ -127,14 +128,16 @@
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
- void showTransient(IntArray types) {
+ @InsetsType int showTransient(IntArray types) {
+ @InsetsType int showingTransientTypes = 0;
boolean changed = false;
for (int i = types.size() - 1; i >= 0; i--) {
final int type = types.get(i);
- if (mShowingTransientTypes.indexOf(type) != -1) {
+ if (!isHidden(type)) {
continue;
}
- if (!isHidden(type)) {
+ showingTransientTypes |= InsetsState.toPublicType(type);
+ if (mShowingTransientTypes.indexOf(type) != -1) {
continue;
}
mShowingTransientTypes.add(type);
@@ -161,6 +164,7 @@
}
});
}
+ return showingTransientTypes;
}
void hideTransient() {
@@ -192,18 +196,6 @@
state = new InsetsState(state);
state.setSourceVisible(mShowingTransientTypes.get(i), false);
}
- if (mFocusedWin != null && getStatusControlTarget(mFocusedWin) == mDummyControlTarget) {
- if (state == originalState) {
- state = new InsetsState(state);
- }
- state.setSourceVisible(ITYPE_STATUS_BAR, mFocusedWin.getRequestedInsetsState());
- }
- if (mFocusedWin != null && getNavControlTarget(mFocusedWin) == mDummyControlTarget) {
- if (state == originalState) {
- state = new InsetsState(state);
- }
- state.setSourceVisible(ITYPE_NAVIGATION_BAR, mFocusedWin.getRequestedInsetsState());
- }
return state;
}
@@ -373,7 +365,7 @@
final WindowState controllingWin =
controlTarget instanceof WindowState ? (WindowState) controlTarget : null;
setVisible(controllingWin == null
- || controllingWin.getRequestedInsetsState().getSource(type).isVisible());
+ || controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type));
}
private void setVisible(boolean visible) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index bded651..1780820 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -233,6 +233,8 @@
// duration of the gesture that is driven by the recents component
targetActivity.mLaunchTaskBehind = true;
mLaunchedTargetActivity = targetActivity;
+ // TODO(b/156772625): Evaluate to send new intents vs. replacing the intent extras.
+ targetActivity.intent.replaceExtras(mTargetIntent);
// Fetch all the surface controls and pass them to the client to get the animation
// started. Cancel any existing recents animation running synchronously (do not hold the
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
index cac992a..3564e0b 100644
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ b/services/core/java/com/android/server/wm/StatusBarController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -90,6 +91,7 @@
View.STATUS_BAR_UNHIDE,
View.STATUS_BAR_TRANSLUCENT,
StatusBarManager.WINDOW_STATUS_BAR,
+ TYPE_STATUS_BAR,
FLAG_TRANSLUCENT_STATUS,
View.STATUS_BAR_TRANSPARENT);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 56ad964..c749125 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2638,7 +2638,7 @@
*/
ActivityStack adjustFocusToNextFocusableTask(String reason) {
return adjustFocusToNextFocusableTask(reason, false /* allowFocusSelf */,
- true /* moveParentsToTop */);
+ true /* moveDisplayToTop */);
}
/** Return the next focusable task by looking from the siblings and parent tasks */
@@ -2661,11 +2661,11 @@
* Find next proper focusable task and make it focused.
* @param reason The reason of making the adjustment.
* @param allowFocusSelf Is the focus allowed to remain on the same task.
- * @param moveParentsToTop Whether to move parents to top while making the task focused.
+ * @param moveDisplayToTop Whether to move display to top while making the task focused.
* @return The root task that now got the focus, {@code null} if none found.
*/
ActivityStack adjustFocusToNextFocusableTask(String reason, boolean allowFocusSelf,
- boolean moveParentsToTop) {
+ boolean moveDisplayToTop) {
ActivityStack focusableTask = (ActivityStack) getNextFocusableTask(allowFocusSelf);
if (focusableTask == null) {
focusableTask = mRootWindowContainer.getNextFocusableStack((ActivityStack) this,
@@ -2676,10 +2676,17 @@
}
final ActivityStack rootTask = (ActivityStack) focusableTask.getRootTask();
- if (!moveParentsToTop) {
- // Only move the next stack to top in its task container.
+ if (!moveDisplayToTop) {
+ // There may be multiple task layers above this task, so when relocating the task to the
+ // top, we should move this task and each of its parent task that below display area to
+ // the top of each layer.
WindowContainer parent = focusableTask.getParent();
- parent.positionChildAt(POSITION_TOP, focusableTask, false /* includingParents */);
+ WindowContainer next = focusableTask;
+ do {
+ parent.positionChildAt(POSITION_TOP, next, false /* includingParents */);
+ next = parent;
+ parent = next.getParent();
+ } while (next.asTask() != null && parent != null);
return rootTask;
}
@@ -2917,7 +2924,12 @@
}
boolean cropWindowsToStackBounds() {
- return isResizeable();
+ // Don't crop HOME/RECENTS windows to stack bounds. This is because in split-screen
+ // they extend past their stack and sysui uses the stack surface to control cropping.
+ // TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
+ final boolean isTopHomeOrRecents = (isActivityTypeHome() || isActivityTypeRecents())
+ && getRootTask().getTopMostTask() == this;
+ return isResizeable() && !isTopHomeOrRecents;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 9c9c61a..2e1e094 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -102,6 +103,9 @@
private ActivityStack mRootPinnedTask;
private ActivityStack mRootSplitScreenPrimaryTask;
+ // TODO(b/159029784): Remove when getStack() behavior is cleaned-up
+ private ActivityStack mRootRecentsTask;
+
private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
@@ -172,6 +176,8 @@
ActivityStack getStack(int windowingMode, int activityType) {
if (activityType == ACTIVITY_TYPE_HOME) {
return mRootHomeTask;
+ } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+ return mRootRecentsTask;
}
if (windowingMode == WINDOWING_MODE_PINNED) {
return mRootPinnedTask;
@@ -208,6 +214,10 @@
return mRootHomeTask;
}
+ @Nullable ActivityStack getRootRecentsTask() {
+ return mRootRecentsTask;
+ }
+
ActivityStack getRootPinnedTask() {
return mRootPinnedTask;
}
@@ -216,6 +226,15 @@
return mRootSplitScreenPrimaryTask;
}
+ ActivityStack getRootSplitScreenSecondaryTask() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).inSplitScreenSecondaryWindowingMode()) {
+ return mChildren.get(i);
+ }
+ }
+ return null;
+ }
+
ArrayList<Task> getVisibleTasks() {
final ArrayList<Task> visibleTasks = new ArrayList<>();
forAllTasks(task -> {
@@ -246,6 +265,16 @@
} else {
mRootHomeTask = stack;
}
+ } else if (stack.isActivityTypeRecents()) {
+ if (mRootRecentsTask != null) {
+ if (!stack.isDescendantOf(mRootRecentsTask)) {
+ throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
+ + mRootRecentsTask + " already exist on display=" + this
+ + " stack=" + stack);
+ }
+ } else {
+ mRootRecentsTask = stack;
+ }
}
if (!stack.isRootTask()) {
@@ -273,6 +302,8 @@
void removeStackReferenceIfNeeded(ActivityStack stack) {
if (stack == mRootHomeTask) {
mRootHomeTask = null;
+ } else if (stack == mRootRecentsTask) {
+ mRootRecentsTask = null;
} else if (stack == mRootPinnedTask) {
mRootPinnedTask = null;
} else if (stack == mRootSplitScreenPrimaryTask) {
@@ -308,8 +339,17 @@
@Override
void positionChildAt(int position, ActivityStack child, boolean includingParents) {
- final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
+ final boolean moveToTop = position >= getChildCount() - 1;
final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
+
+ // Reset mPreferredTopFocusableStack before positioning to top or {@link
+ // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+ // resumed activity.
+ final boolean wasContained = mChildren.contains(child);
+ if (moveToTop && wasContained && child.isFocusable()) {
+ mPreferredTopFocusableStack = null;
+ }
+
if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
// This stack is always-on-top, override the default behavior.
Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
@@ -339,6 +379,17 @@
child.updateTaskMovement(moveToTop);
mDisplayContent.setLayoutNeeded();
+
+ // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+ // the original position is preferred to be top, the stack should have higher priority when
+ // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+ // preferred stack is set only when moving an existing stack to top instead of adding a new
+ // stack that may be too early (e.g. in the middle of launching or reparenting).
+ if (moveToTop && child.isFocusableAndVisible()) {
+ mPreferredTopFocusableStack = child;
+ } else if (mPreferredTopFocusableStack == child) {
+ mPreferredTopFocusableStack = null;
+ }
}
/**
@@ -736,29 +787,10 @@
"positionStackAt: Can only have one task on display=" + this);
}
- final boolean movingToTop = wasContained && position >= getStackCount() - 1;
- // Reset mPreferredTopFocusableStack before positioning to top or {@link
- // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
- // resumed activity.
- if (movingToTop && stack.isFocusable()) {
- mPreferredTopFocusableStack = null;
- }
-
// Since positionChildAt() is called during the creation process of pinned stacks,
// ActivityStack#getStack() can be null.
positionStackAt(position, stack, includingParents);
- // The insert position may be adjusted to non-top when there is always-on-top stack. Since
- // the original position is preferred to be top, the stack should have higher priority when
- // we are looking for top focusable stack. The condition {@code wasContained} restricts the
- // preferred stack is set only when moving an existing stack to top instead of adding a new
- // stack that may be too early (e.g. in the middle of launching or reparenting).
- if (movingToTop && stack.isFocusableAndVisible()) {
- mPreferredTopFocusableStack = stack;
- } else if (mPreferredTopFocusableStack == stack) {
- mPreferredTopFocusableStack = null;
- }
-
if (updateLastFocusedStackReason != null) {
final ActivityStack currentFocusedStack = getFocusedStack();
if (currentFocusedStack != prevFocusedStack) {
@@ -1797,21 +1829,23 @@
// reparenting stack finished.
// Keep the order from bottom to top.
int numStacks = getStackCount();
+
+ final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated();
+ final ActivityStack rootStack = splitScreenActivated ? toDisplayArea
+ .getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null;
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
final ActivityStack stack = getStackAt(stackNdx);
// Always finish non-standard type stacks.
if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
stack.finishAllActivitiesImmediately();
} else {
- // If default display is in split-window mode, set windowing mode of the
- // stack to split-screen secondary. Otherwise, set the windowing mode to
- // undefined by default to let stack inherited the windowing mode from the
- // new display.
- final int windowingMode = toDisplayArea.isSplitScreenModeActivated()
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_UNDEFINED;
- stack.reparent(toDisplayArea, true /* onTop */);
- stack.setWindowingMode(windowingMode);
+ // Reparent the stack to the root task of secondary-split-screen or display area.
+ stack.reparent(stack.supportsSplitScreenWindowingMode() && rootStack != null
+ ? rootStack : toDisplayArea, POSITION_TOP);
+
+ // Set the windowing mode to undefined by default to let the stack inherited the
+ // windowing mode.
+ stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
lastReparentedStack = stack;
}
// Stacks may be removed from this display. Ensure each stack will be processed
@@ -1819,6 +1853,17 @@
stackNdx -= numStacks - getStackCount();
numStacks = getStackCount();
}
+ if (lastReparentedStack != null && splitScreenActivated) {
+ if (!lastReparentedStack.supportsSplitScreenWindowingMode()) {
+ mAtmService.getTaskChangeNotificationController()
+ .notifyActivityDismissingDockedStack();
+ toDisplayArea.onSplitScreenModeDismissed(lastReparentedStack);
+ } else if (rootStack != null) {
+ // update focus
+ rootStack.moveToFront("display-removed");
+ }
+ }
+
mRemoved = true;
return lastReparentedStack;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 32717d0..6bbc019 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1529,6 +1529,29 @@
}
/**
+ * This is a form of rectangle "difference". It cut off each dimension of rect by the amount
+ * that toRemove is "pushing into" it from the outside. Any dimension that fully contains
+ * toRemove won't change.
+ */
+ private void cutRect(Rect rect, Rect toRemove) {
+ if (toRemove.isEmpty()) return;
+ if (toRemove.top < rect.bottom && toRemove.bottom > rect.top) {
+ if (toRemove.right >= rect.right && toRemove.left >= rect.left) {
+ rect.right = toRemove.left;
+ } else if (toRemove.left <= rect.left && toRemove.right <= rect.right) {
+ rect.left = toRemove.right;
+ }
+ }
+ if (toRemove.left < rect.right && toRemove.right > rect.left) {
+ if (toRemove.bottom >= rect.bottom && toRemove.top >= rect.top) {
+ rect.bottom = toRemove.top;
+ } else if (toRemove.top <= rect.top && toRemove.bottom <= rect.bottom) {
+ rect.top = toRemove.bottom;
+ }
+ }
+ }
+
+ /**
* Retrieves the visible bounds of the window.
* @param bounds The rect which gets the bounds.
*/
@@ -1544,6 +1567,20 @@
} else {
intersectWithStackBounds = false;
}
+ if (inSplitScreenPrimaryWindowingMode()) {
+ // If this is in the primary split and the home stack is the top visible task in
+ // the secondary split, it means this is "minimized" and thus must prevent
+ // overlapping with home.
+ // TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
+ final ActivityStack rootSecondary =
+ task.getDisplayArea().getRootSplitScreenSecondaryTask();
+ if (rootSecondary.isActivityTypeHome() || rootSecondary.isActivityTypeRecents()) {
+ final WindowContainer topTask = rootSecondary.getTopChild();
+ if (topTask.isVisible()) {
+ cutRect(mTmpRect, topTask.getBounds());
+ }
+ }
+ }
}
bounds.set(mWindowFrames.mVisibleFrame);
@@ -3576,6 +3613,9 @@
@Override
public void notifyInsetsControlChanged() {
ProtoLog.d(WM_DEBUG_IME, "notifyInsetsControlChanged for %s ", this);
+ if (mAppDied || mRemoved) {
+ return;
+ }
final InsetsStateController stateController =
getDisplayContent().getInsetsStateController();
try {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d257002..86aacf3 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -48,6 +48,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
import android.view.DisplayInfo;
@@ -124,7 +125,7 @@
private static class FixedRotationTransformState {
final DisplayInfo mDisplayInfo;
final DisplayFrames mDisplayFrames;
- final InsetsState mInsetsState;
+ final InsetsState mInsetsState = new InsetsState();
final Configuration mRotatedOverrideConfiguration;
final SeamlessRotator mRotator;
/**
@@ -133,14 +134,14 @@
*/
final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3);
final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
+ final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
boolean mIsTransforming = true;
FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
- DisplayFrames rotatedDisplayFrames, InsetsState rotatedInsetsState,
- Configuration rotatedConfig, int currentRotation) {
+ DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig,
+ int currentRotation) {
mDisplayInfo = rotatedDisplayInfo;
mDisplayFrames = rotatedDisplayFrames;
- mInsetsState = rotatedInsetsState;
mRotatedOverrideConfiguration = rotatedConfig;
// This will use unrotate as rotate, so the new and old rotation are inverted.
mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation,
@@ -516,6 +517,12 @@
: null;
}
+ Rect getFixedRotationBarContentFrame(int windowType) {
+ return isFixedRotationTransforming()
+ ? mFixedRotationTransformState.mBarContentFrames.get(windowType)
+ : null;
+ }
+
InsetsState getFixedRotationTransformInsetsState() {
return isFixedRotationTransforming() ? mFixedRotationTransformState.mInsetsState : null;
}
@@ -526,12 +533,12 @@
if (mFixedRotationTransformState != null) {
return;
}
- final InsetsState insetsState = new InsetsState();
- mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames, insetsState,
- mDisplayContent.getConfiguration().uiMode);
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
- insetsState, new Configuration(config), mDisplayContent.getRotation());
+ new Configuration(config), mDisplayContent.getRotation());
mFixedRotationTransformState.mAssociatedTokens.add(this);
+ mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
+ mFixedRotationTransformState.mInsetsState,
+ mFixedRotationTransformState.mBarContentFrames);
onConfigurationChanged(getParent().getConfiguration());
notifyFixedRotationTransform(true /* enabled */);
}
@@ -554,6 +561,25 @@
notifyFixedRotationTransform(true /* enabled */);
}
+ /**
+ * Return {@code true} if one of the associated activity is still animating. Otherwise,
+ * return {@code false}.
+ */
+ boolean hasAnimatingFixedRotationTransition() {
+ if (mFixedRotationTransformState == null) {
+ return false;
+ }
+
+ for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) {
+ final ActivityRecord r =
+ mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
+ if (r != null && r.isAnimating(TRANSITION | PARENTS)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void finishFixedRotationTransform() {
finishFixedRotationTransform(null /* applyDisplayRotation */);
}
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index e790a19..7132706 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -165,6 +165,11 @@
return ok();
}
+binder::Status BinderIncrementalService::disableReadLogs(int32_t storageId) {
+ mImpl.disableReadLogs(storageId);
+ return ok();
+}
+
binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path,
int32_t* _aidl_return) {
*_aidl_return = mImpl.makeDir(storageId, path);
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 68549f5..1015494 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -74,7 +74,7 @@
std::vector<uint8_t>* _aidl_return) final;
binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
binder::Status deleteStorage(int32_t storageId) final;
-
+ binder::Status disableReadLogs(int32_t storageId) final;
binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
const std::string& libDirRelativePath,
const std::string& abi, bool extractNativeLibs,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 885f4d2..3450c3a 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -60,6 +60,7 @@
static constexpr auto storagePrefix = "st"sv;
static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
static constexpr auto infoMdName = ".info"sv;
+ static constexpr auto readLogsDisabledMarkerName = ".readlogs_disabled"sv;
static constexpr auto libDir = "lib"sv;
static constexpr auto libSuffix = ".so"sv;
static constexpr auto blockSize = 4096;
@@ -172,6 +173,13 @@
return name;
}
+
+static bool checkReadLogsDisabledMarker(std::string_view root) {
+ const auto markerPath = path::c_str(path::join(root, constants().readLogsDisabledMarkerName));
+ struct stat st;
+ return (::stat(markerPath, &st) == 0);
+}
+
} // namespace
IncrementalService::IncFsMount::~IncFsMount() {
@@ -618,6 +626,32 @@
return it->second->second.storage;
}
+void IncrementalService::disableReadLogs(StorageId storageId) {
+ std::unique_lock l(mLock);
+ const auto ifs = getIfsLocked(storageId);
+ if (!ifs) {
+ LOG(ERROR) << "disableReadLogs failed, invalid storageId: " << storageId;
+ return;
+ }
+ if (!ifs->readLogsEnabled()) {
+ return;
+ }
+ ifs->disableReadLogs();
+ l.unlock();
+
+ const auto metadata = constants().readLogsDisabledMarkerName;
+ if (auto err = mIncFs->makeFile(ifs->control,
+ path::join(ifs->root, constants().mount,
+ constants().readLogsDisabledMarkerName),
+ 0777, idFromMetadata(metadata), {})) {
+ //{.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
+ LOG(ERROR) << "Failed to make marker file for storageId: " << storageId;
+ return;
+ }
+
+ setStorageParams(storageId, /*enableReadLogs=*/false);
+}
+
int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
const auto ifs = getIfs(storageId);
if (!ifs) {
@@ -627,6 +661,11 @@
const auto& params = ifs->dataLoaderStub->params();
if (enableReadLogs) {
+ if (!ifs->readLogsEnabled()) {
+ LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId;
+ return -EPERM;
+ }
+
if (auto status = mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage,
params.packageName.c_str());
!status.isOk()) {
@@ -1072,6 +1111,11 @@
std::move(control), *this);
cleanupFiles.release(); // ifs will take care of that now
+ // Check if marker file present.
+ if (checkReadLogsDisabledMarker(root)) {
+ ifs->disableReadLogs();
+ }
+
std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints;
auto d = openDir(root);
while (auto e = ::readdir(d.get())) {
@@ -1243,6 +1287,11 @@
ifs->mountId = mount.storage().id();
mNextId = std::max(mNextId, ifs->mountId + 1);
+ // Check if marker file present.
+ if (checkReadLogsDisabledMarker(mountTarget)) {
+ ifs->disableReadLogs();
+ }
+
// DataLoader params
DataLoaderParamsParcel dataLoaderParams;
{
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 918531b..a6cc946 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -94,6 +94,10 @@
Permanent = 1,
};
+ enum StorageFlags {
+ ReadLogsEnabled = 1,
+ };
+
static FileId idFromMetadata(std::span<const uint8_t> metadata);
static inline FileId idFromMetadata(std::span<const char> metadata) {
return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
@@ -116,6 +120,7 @@
int unbind(StorageId storage, std::string_view target);
void deleteStorage(StorageId storage);
+ void disableReadLogs(StorageId storage);
int setStorageParams(StorageId storage, bool enableReadLogs);
int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -264,6 +269,7 @@
const std::string root;
Control control;
/*const*/ MountId mountId;
+ int32_t flags = StorageFlags::ReadLogsEnabled;
StorageMap storages;
BindMap bindPoints;
DataLoaderStubPtr dataLoaderStub;
@@ -282,6 +288,9 @@
StorageMap::iterator makeStorage(StorageId id);
+ void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; }
+ int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
+
static void cleanupFilesystem(std::string_view root);
};
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 26b5094..1ae9e25 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -929,6 +929,34 @@
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
}
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
+ mVold->mountIncFsSuccess();
+ mIncFs->makeFileSuccess();
+ mVold->bindMountSuccess();
+ mVold->setIncFsMountOptionsSuccess();
+ mDataLoaderManager->bindToDataLoaderSuccess();
+ mDataLoaderManager->getDataLoaderSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // Enabling and then disabling readlogs.
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ // After setIncFsMountOptions succeeded expecting to start watching.
+ EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+ // Not expecting callback removal.
+ EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+ TemporaryDir tempDir;
+ int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew,
+ {}, {}, {});
+ ASSERT_GE(storageId, 0);
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ // Now disable.
+ mIncrementalService->disableReadLogs(storageId);
+ ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+}
+
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3b38d94..6db3233 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -80,6 +80,7 @@
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
+ <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 3062584..b100c84 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -31,9 +31,10 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+
+import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import com.android.server.wm.WindowManagerInternal;
@@ -55,7 +56,6 @@
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -65,6 +65,7 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -100,6 +101,7 @@
private BroadcastReceiver mTimeChangedCallback;
private AlarmManager.OnAlarmListener mCustomListener;
private Consumer<PowerSaveState> mPowerSaveConsumer;
+ private TwilightListener mTwilightListener;
@Before
public void setUp() {
@@ -107,6 +109,10 @@
when(mContext.checkCallingOrSelfPermission(anyString()))
.thenReturn(PackageManager.PERMISSION_GRANTED);
doAnswer(inv -> {
+ mTwilightListener = (TwilightListener) inv.getArgument(0);
+ return null;
+ }).when(mTwilightManager).registerListener(any(), any());
+ doAnswer(inv -> {
mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1);
return null;
}).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any());
@@ -160,6 +166,37 @@
}
@Test
+ public void setNightMoveActivated_overridesFunctionCorrectly() throws RemoteException {
+ // set up
+ when(mPowerManager.isInteractive()).thenReturn(false);
+ mService.setNightMode(MODE_NIGHT_NO);
+ assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+
+ // assume it is day time
+ doReturn(false).when(mTwilightState).isNight();
+
+ // set mode to auto
+ mService.setNightMode(MODE_NIGHT_AUTO);
+
+ // set night mode on overriding current config
+ mService.setNightModeActivated(true);
+
+ assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
+
+ // now it is night time
+ doReturn(true).when(mTwilightState).isNight();
+ mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+ assertTrue(mUiManagerService.getConfiguration().isNightModeActive());
+
+ // now it is next day mid day
+ doReturn(false).when(mTwilightState).isNight();
+ mTwilightListener.onTwilightStateChanged(mTwilightState);
+
+ assertFalse(mUiManagerService.getConfiguration().isNightModeActive());
+ }
+
+ @Test
public void setAutoMode_screenOffRegistered() throws RemoteException {
try {
mService.setNightMode(MODE_NIGHT_NO);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 4ee933a..29b96eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -794,6 +794,39 @@
}
/**
+ * Verify that when top focused activity is on secondary display, when finishing the top focused
+ * activity on default display, the preferred top stack on default display should be changed by
+ * adjusting focus.
+ */
+ @Test
+ public void testFinishActivityIfPossible_PreferredTopStackChanged() {
+ final ActivityRecord topActivityOnNonTopDisplay =
+ createActivityOnDisplay(true /* defaultDisplay */, null /* process */);
+ ActivityStack topRootableTask = topActivityOnNonTopDisplay.getRootTask();
+ topRootableTask.moveToFront("test");
+ assertTrue(topRootableTask.isTopStackInDisplayArea());
+ assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea()
+ .mPreferredTopFocusableStack);
+
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+ topRootableTask = secondaryDisplayActivity.getRootTask();
+ topRootableTask.moveToFront("test");
+ assertTrue(topRootableTask.isTopStackInDisplayArea());
+ assertEquals(topRootableTask,
+ secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableStack);
+
+ // The global top focus activity is on secondary display now.
+ // Finish top activity on default display and verify the next preferred top focusable stack
+ // on default display has changed.
+ topActivityOnNonTopDisplay.setState(RESUMED, "test");
+ topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */,
+ null /* resultGrants */, "test", false /* oomAdj */);
+ assertEquals(mTask, mStack.getTopMostTask());
+ assertEquals(mStack, mActivity.getDisplayArea().mPreferredTopFocusableStack);
+ }
+
+ /**
* Verify that resumed activity is paused due to finish request.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index ef28a45..a16bd2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -38,6 +38,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -452,6 +453,32 @@
assertFalse(middle.isVisible());
}
+ @Test
+ public void testTransferStartingWindowSetFixedRotation() {
+ mWm.mIsFixedRotationTransformEnabled = true;
+ final ActivityRecord topActivity = createTestActivityRecordForGivenTask(mTask);
+ mTask.positionChildAt(topActivity, POSITION_TOP);
+ mActivity.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false);
+ waitUntilHandlersIdle();
+
+ // Make activities to have different rotation from it display and set fixed rotation
+ // transform to activity1.
+ int rotation = (mDisplayContent.getRotation() + 1) % 4;
+ mDisplayContent.setFixedRotationLaunchingApp(mActivity, rotation);
+ doReturn(rotation).when(mDisplayContent)
+ .rotationForActivityInDifferentOrientation(topActivity);
+
+ // Make sure the fixed rotation transform linked to activity2 when adding starting window
+ // on activity2.
+ topActivity.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, mActivity.appToken.asBinder(),
+ false, false, false, true, false);
+ waitUntilHandlersIdle();
+ assertTrue(topActivity.hasFixedRotationTransform());
+ }
+
private ActivityRecord createIsolatedTestActivityRecord() {
final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(taskStack, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4ad7dff..d3f677c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1144,7 +1144,14 @@
assertTrue(app.hasFixedRotationTransform(app2));
assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
+ // The fixed rotation transform can only be finished when all animation finished.
+ doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
+ mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
+ assertTrue(app.hasFixedRotationTransform());
+ assertTrue(app2.hasFixedRotationTransform());
+
// The display should be rotated after the launch is finished.
+ doReturn(false).when(app).isAnimating(anyInt(), anyInt());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
// The fixed rotation should be cleared and the new rotation is applied to display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 27c4e9b..1922351 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -57,6 +57,7 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.InsetsState;
@@ -776,15 +777,15 @@
}
private void assertSimulateLayoutSameDisplayFrames() {
- final int uiMode = 0;
final String prefix = "";
final InsetsState simulatedInsetsState = new InsetsState();
final DisplayFrames simulatedDisplayFrames = createDisplayFrames();
- mDisplayPolicy.beginLayoutLw(mFrames, uiMode);
+ mDisplayPolicy.beginLayoutLw(mFrames, mDisplayContent.getConfiguration().uiMode);
// Force the display bounds because it is not synced with display frames in policy test.
mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted);
mDisplayContent.getInsetsStateController().onPostLayout();
- mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, simulatedInsetsState, uiMode);
+ mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, simulatedInsetsState,
+ new SparseArray<>() /* barContentFrames */);
final StringWriter realFramesDump = new StringWriter();
mFrames.dump(prefix, new PrintWriter(realFramesDump));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index ca6679d..243468a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -349,11 +349,19 @@
assertEquals(Configuration.ORIENTATION_PORTRAIT,
homeActivity.getConfiguration().orientation);
- // Home activity won't become top (return to landActivity), so its fixed rotation and the
- // top rotated record should be cleared.
+ // Home activity won't become top (return to landActivity), so the top rotated record should
+ // be cleared.
mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
- assertFalse(homeActivity.hasFixedRotationTransform());
+ assertFalse(mDefaultDisplay.isFixedRotationLaunchingApp(homeActivity));
assertFalse(mDefaultDisplay.hasTopFixedRotationLaunchingApp());
+ // The transform should keep until the transition is done, so the restored configuration
+ // won't be sent to activity and cause unnecessary configuration change.
+ assertTrue(homeActivity.hasFixedRotationTransform());
+
+ // In real case the transition will be executed from RecentsAnimation#finishAnimation.
+ mDefaultDisplay.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(
+ homeActivity.token);
+ assertFalse(homeActivity.hasFixedRotationTransform());
}
@Test
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 665cf83..15b395c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -24,6 +24,7 @@
import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -288,14 +289,29 @@
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
- // The configuration bounds should keep the same.
+ // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
assertEquals(origWidth, configBounds.width());
assertEquals(origHeight, configBounds.height());
assertScaled();
+ final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
- assertEquals(newDisplay.getBounds().height() - notchHeight,
+ assertEquals(newDisplayBounds.height() - notchHeight,
(int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
+
+ // Recompute the natural configuration in the new display.
+ mActivity.clearSizeCompatMode();
+ mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
+ // Because the display cannot rotate, the portrait activity will fit the short side of
+ // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
+ assertEquals(newDisplayBounds.height(), configBounds.height());
+ assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ configBounds.width());
+ assertFitted();
+ // The appBounds should be [200, 100 - 700, 1000].
+ final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ assertEquals(configBounds.width(), appBounds.width());
+ assertEquals(configBounds.height() - notchHeight, appBounds.height());
}
@Test
@@ -491,7 +507,10 @@
mService.mWindowManager.mIsFixedRotationTransformEnabled = true;
final int dw = 1000;
final int dh = 2500;
- setUpDisplaySizeWithApp(dw, dh);
+ final int notchHeight = 200;
+ setUpApp(new TestDisplayContent.Builder(mService, dw, dh).setNotch(notchHeight).build());
+ addStatusBar(mActivity.mDisplayContent);
+
mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_OPEN,
false /* alwaysKeepCurrent */);
mActivity.mDisplayContent.mOpeningApps.add(mActivity);
@@ -503,31 +522,76 @@
// Display keeps in original orientation.
assertEquals(Configuration.ORIENTATION_PORTRAIT,
mActivity.mDisplayContent.getConfiguration().orientation);
- // Activity bounds should be [350, 0 - 2150, 1000] in landscape. Its width=1000*1.8=1800.
+ // The width should be restricted by the max aspect ratio = 1000 * 1.8 = 1800.
assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
- // The bounds should be horizontal centered: (2500-1900)/2=350.
- assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);
+ // The notch is at the left side of the landscape activity. The bounds should be horizontal
+ // centered in the remaining area [200, 0 - 2500, 1000], so its left should be
+ // 200 + (2300 - 1800) / 2 = 450. The bounds should be [450, 0 - 2250, 1000].
+ assertEquals(notchHeight + (dh - notchHeight - mActivity.getBounds().width()) / 2,
+ mActivity.getBounds().left);
// The letterbox needs a main window to layout.
- addWindowToActivity(mActivity);
+ final WindowState w = addWindowToActivity(mActivity);
// Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
mActivity.mRootWindowContainer.performSurfacePlacement();
- // The letterbox insets should be [350, 0 - 350, 0].
+ // The letterbox insets should be [450, 0 - 250, 0].
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
+
+ final StatusBarController statusBarController =
+ mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
+ // The activity doesn't fill the display, so the letterbox of the rotated activity is
+ // overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
+ // be transparent.
+ assertFalse(statusBarController.isTransparentAllowed(w));
+
+ // Make the activity fill the display.
+ prepareUnresizable(10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
+ w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ // Refresh the letterbox.
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ // The letterbox should only cover the notch area, so status bar can be transparent.
+ assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
+ assertTrue(statusBarController.isTransparentAllowed(w));
}
- private WindowState addWindowToActivity(ActivityRecord activity) {
+ private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
- mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+ activity.mWmService, mock(Session.class), new TestIWindow(), params, activity);
WindowTestsBase.makeWindowVisible(w);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
- mActivity.addWindow(w);
+ activity.addWindow(w);
return w;
}
+ private static void addStatusBar(DisplayContent displayContent) {
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+ doReturn(true).when(displayPolicy).hasStatusBar();
+ displayPolicy.onConfigurationChanged();
+
+ final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR, displayContent);
+ final WindowManager.LayoutParams attrs =
+ new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_STATUS_BAR);
+ attrs.gravity = android.view.Gravity.TOP;
+ attrs.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ attrs.setFitInsetsTypes(0 /* types */);
+ final WindowTestUtils.TestWindowState statusBar = new WindowTestUtils.TestWindowState(
+ displayContent.mWmService, mock(Session.class), new TestIWindow(), attrs, token);
+ token.addWindow(statusBar);
+ statusBar.setRequestedSize(displayContent.mBaseDisplayWidth,
+ displayContent.getDisplayUiContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height));
+
+ displayPolicy.addWindowLw(statusBar, attrs);
+ displayPolicy.beginLayoutLw(displayContent.mDisplayFrames,
+ displayContent.getConfiguration().uiMode);
+ }
+
/**
* Setup {@link #mActivity} as a size-compat-mode-able activity with fixed aspect and/or
* orientation.