diff options
Diffstat (limited to 'packages')
88 files changed, 1170 insertions, 261 deletions
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index a61881f2f0df..95ec83dd9e14 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -181,7 +181,8 @@ public class CaptivePortalLoginActivity extends Activity { } private void done(boolean success) { - if (DBG) logd(String.format("Result success %b for %s", success, mUrl.toString())); + if (DBG) logd(String.format("Result success %b for %s", success, + mUrl != null ? mUrl.toString() : "null")); if (success) { // Trigger re-evaluation upon success http response code CarrierActionUtils.applyCarrierAction( diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index 14861c261f0d..172490dc9178 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -84,5 +84,16 @@ Copyright (C) 2016 The Android Open Source Project <action android:name="android.service.quicksettings.action.QS_TILE" /> </intent-filter> </service> + + <!-- FileProvider for sending pictures --> + <provider + android:name="android.support.v4.content.FileProvider" + android:authorities="com.android.egg.fileprovider" + android:grantUriPermissions="true" + android:exported="false"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/filepaths" /> + </provider> </application> </manifest> diff --git a/packages/EasterEgg/res/drawable/food_cookie.xml b/packages/EasterEgg/res/drawable/food_cookie.xml new file mode 100644 index 000000000000..74dd134355e2 --- /dev/null +++ b/packages/EasterEgg/res/drawable/food_cookie.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2017 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <group> + <path + android:fillColor="#55FFFFFF" + android:fillType="evenOdd" + android:pathData="M5.71 18.29A8.99 8.99 0 0 0 22 13c0-3-1.46-5.65-3.71-7.29A8.99 8.99 0 0 0 2 11c0 3 1.46 5.65 3.71 7.29z"/> + <path + android:fillColor="#FFFFFFFF" + android:fillType="evenOdd" + android:pathData="M7.25 19.18A8.5 8.5 0 0 0 19.19 7.24 9 9 0 0 1 7.24 19.19z"/> + <path + android:fillColor="#55FFFFFF" + android:pathData="M10.5 3a0.5 0.5 0 1 1 1 0v2.05a0.5 0.5 0 1 1-1 0V3zm3.1 0.42a0.5 0.5 0 0 1 0.93 0.39l-0.8 1.88A0.5 0.5 0 1 1 12.8 5.3l0.8-1.88zm2.7 1.57a0.5 0.5 0 1 1 0.71 0.7l-1.45 1.46a0.5 0.5 0 0 1-0.7-0.71l1.44-1.45zm1.9 2.5a0.5 0.5 0 0 1 0.38 0.92l-1.9 0.77a0.5 0.5 0 0 1-0.37-0.93l1.9-0.77zM19 10.5a0.5 0.5 0 1 1 0 1h-2.05a0.5 0.5 0 0 1 0-1H19zm-0.42 3.1a0.5 0.5 0 0 1-0.39 0.93l-1.88-0.8a0.5 0.5 0 1 1 0.39-0.92l1.88 0.8zm-1.57 2.7a0.5 0.5 0 1 1-0.7 0.71l-1.46-1.45a0.5 0.5 0 0 1 0.71-0.7l1.45 1.44zm-2.5 1.9a0.5 0.5 0 1 1-0.92 0.38l-0.77-1.9a0.5 0.5 0 0 1 0.93-0.37l0.77 1.9zM11.5 19a0.5 0.5 0 1 1-1 0v-2.05a0.5 0.5 0 0 1 1 0V19zm-3.1-0.42a0.5 0.5 0 0 1-0.93-0.39l0.8-1.88A0.5 0.5 0 0 1 9.2 16.7l-0.8 1.88zm-2.7-1.57a0.5 0.5 0 1 1-0.71-0.7l1.45-1.46a0.5 0.5 0 0 1 0.7 0.71L5.7 17.01zm-1.9-2.48a0.5 0.5 0 0 1-0.38-0.92l1.88-0.8a0.5 0.5 0 0 1 0.4 0.92l-1.9 0.8zM3 11.5a0.5 0.5 0 1 1 0-1h2.05a0.5 0.5 0 1 1 0 1H3zm0.42-3.1A0.5 0.5 0 0 1 3.8 7.46l1.88 0.8A0.5 0.5 0 1 1 5.3 9.2L3.42 8.4zm1.57-2.7a0.5 0.5 0 1 1 0.7-0.71l1.46 1.45a0.5 0.5 0 0 1-0.71 0.7L4.99 5.7zm2.5-1.9A0.5 0.5 0 0 1 8.4 3.41l0.77 1.9a0.5 0.5 0 0 1-0.93 0.37L7.48 3.8z"/> + </group> +</vector>
\ No newline at end of file diff --git a/packages/EasterEgg/res/values/strings.xml b/packages/EasterEgg/res/values/strings.xml index 8478a438a0e4..61e38342872e 100644 --- a/packages/EasterEgg/res/values/strings.xml +++ b/packages/EasterEgg/res/values/strings.xml @@ -17,6 +17,7 @@ Copyright (C) 2016 The Android Open Source Project <resources xmlns:android="http://schemas.android.com/apk/res/android"> <string name="app_name" translatable="false">Android Easter Egg</string> <string name="notification_name" translatable="false">Android Neko</string> + <string name="notification_channel_name" translatable="false">New cats</string> <string name="default_tile_name" translatable="false">\????</string> <string name="notification_title" translatable="false">A cat is here.</string> <string name="default_cat_name" translatable="false">Cat #%s</string> @@ -34,7 +35,7 @@ Copyright (C) 2016 The Android Open Source Project <item>@drawable/food_bits</item> <item>@drawable/food_sysuituna</item> <item>@drawable/food_chicken</item> - <item>@drawable/food_donut</item> + <item>@drawable/food_cookie</item> </array> <integer-array name="food_intervals"> <item>0</item> diff --git a/packages/EasterEgg/res/xml/filepaths.xml b/packages/EasterEgg/res/xml/filepaths.xml new file mode 100644 index 000000000000..2130025e9265 --- /dev/null +++ b/packages/EasterEgg/res/xml/filepaths.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2017 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. +--> +<paths> + <external-path name="cats" path="Pictures/Cats" /> +</paths>
\ No newline at end of file diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java index a4df372ef835..dd1bd07f3298 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java +++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java @@ -31,6 +31,8 @@ import java.util.concurrent.ThreadLocalRandom; import com.android.egg.R; import com.android.internal.logging.MetricsLogger; +import static com.android.egg.neko.NekoLand.CHAN_ID; + public class Cat extends Drawable { public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40}; @@ -218,6 +220,7 @@ public class Cat extends Drawable { .setContentText(getName()) .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)) .setAutoCancel(true) + .setChannel(CHAN_ID) .setVibrate(PURR) .addExtras(extras); } diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java index 689e38142988..d2e37d8779d1 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore.Images; +import android.support.v4.content.FileProvider; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -56,6 +57,8 @@ import java.util.Comparator; import java.util.List; public class NekoLand extends Activity implements PrefsListener { + public static String CHAN_ID = "EGG"; + public static boolean DEBUG = false; public static boolean DEBUG_NOTIFICATIONS = false; @@ -289,10 +292,13 @@ public class NekoLand extends Activity implements PrefsListener { new String[] {png.toString()}, new String[] {"image/png"}, null); - Uri uri = Uri.fromFile(png); + Log.v("Neko", "cat file: " + png); + Uri uri = FileProvider.getUriForFile(this, "com.android.egg.fileprovider", png); + Log.v("Neko", "cat uri: " + uri); Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_STREAM, uri); intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName()); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setType("image/png"); startActivity(Intent.createChooser(intent, null)); cat.logShare(this); diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java index 808ec361fb4f..42506e61d2ec 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java @@ -15,15 +15,15 @@ package com.android.egg.neko; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import java.util.List; @@ -33,6 +33,9 @@ import com.android.egg.R; import java.util.Random; +import static com.android.egg.neko.Cat.PURR; +import static com.android.egg.neko.NekoLand.CHAN_ID; + public class NekoService extends JobService { private static final String TAG = "NekoService"; @@ -40,6 +43,7 @@ public class NekoService extends JobService { public static int JOB_ID = 42; public static int CAT_NOTIFICATION = 1; + public static int DEBUG_NOTIFICATION = 1234; public static float CAT_CAPTURE_PROB = 1.0f; // generous @@ -50,6 +54,18 @@ public class NekoService extends JobService { public static float INTERVAL_JITTER_FRAC = 0.25f; + private static void setupNotificationChannels(Context context) { + NotificationManager noman = context.getSystemService(NotificationManager.class); + NotificationChannel eggChan = new NotificationChannel(CHAN_ID, + context.getString(R.string.notification_channel_name), + NotificationManager.IMPORTANCE_DEFAULT); + eggChan.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT); // cats are quiet + eggChan.setVibrationPattern(PURR); // not totally quiet though + eggChan.setBlockableSystem(true); // unlike a real cat, you can push this one off your lap + eggChan.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); // cats sit in the window + noman.createNotificationChannel(eggChan); + } + @Override public boolean onStartJob(JobParameters params) { Log.v(TAG, "Starting job: " + String.valueOf(params)); @@ -64,8 +80,9 @@ public class NekoService extends JobService { final Notification.Builder builder = cat.buildNotification(this) .setContentTitle("DEBUG") + .setChannel(NekoLand.CHAN_ID) .setContentText("Ran job: " + params); - noman.notify(1, builder.build()); + noman.notify(DEBUG_NOTIFICATION, builder.build()); } final PrefState prefs = new PrefState(this); @@ -111,6 +128,8 @@ public class NekoService extends JobService { } public static void registerJob(Context context, long intervalMinutes) { + setupNotificationChannels(context); + JobScheduler jss = context.getSystemService(JobScheduler.class); jss.cancel(JOB_ID); long interval = intervalMinutes * MINUTES; @@ -126,12 +145,13 @@ public class NekoService extends JobService { if (NekoLand.DEBUG_NOTIFICATIONS) { NotificationManager noman = context.getSystemService(NotificationManager.class); - noman.notify(500, new Notification.Builder(context) + noman.notify(DEBUG_NOTIFICATION, new Notification.Builder(context) .setSmallIcon(R.drawable.stat_icon) .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES))) .setContentText(String.valueOf(jobInfo)) .setPriority(Notification.PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) + .setChannel(NekoLand.CHAN_ID) .setShowWhen(true) .build()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index bf1bf34dc220..40c2b1f3b771 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -339,26 +339,35 @@ public class ApplicationsState { synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(userId).get(packageName); if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { - mBackgroundHandler.post(() -> { - try { - final StorageStats stats = mStats.queryStatsForPackage( - entry.info.storageUuid, packageName, UserHandle.of(userId)); - final PackageStats legacy = new PackageStats(packageName, userId); - legacy.codeSize = stats.getCodeBytes(); - legacy.dataSize = stats.getDataBytes(); - legacy.cacheSize = stats.getCacheBytes(); - try { - mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true); - } catch (RemoteException ignored) { - } - } catch (NameNotFoundException | IOException e) { - Log.w(TAG, "Failed to query stats: " + e); - try { - mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false); - } catch (RemoteException ignored) { - } - } - }); + mBackgroundHandler.post( + () -> { + try { + final StorageStats stats = + mStats.queryStatsForPackage( + entry.info.storageUuid, + packageName, + UserHandle.of(userId)); + final long cacheQuota = + mStats.getCacheQuotaBytes( + entry.info.storageUuid.toString(), entry.info.uid); + final PackageStats legacy = new PackageStats(packageName, userId); + legacy.codeSize = stats.getCodeBytes(); + legacy.dataSize = stats.getDataBytes(); + legacy.cacheSize = Math.min(stats.getCacheBytes(), cacheQuota); + try { + mBackgroundHandler.mStatsObserver.onGetStatsCompleted( + legacy, true); + } catch (RemoteException ignored) { + } + } catch (NameNotFoundException | IOException e) { + Log.w(TAG, "Failed to query stats: " + e); + try { + mBackgroundHandler.mStatsObserver.onGetStatsCompleted( + null, false); + } catch (RemoteException ignored) { + } + } + }); } if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock"); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 109446d8ed4a..2873fb68ce5b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -335,8 +335,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public int getProfileConnectionState(LocalBluetoothProfile profile) { - if (mProfileConnectionState == null || - mProfileConnectionState.get(profile) == null) { + if (mProfileConnectionState.get(profile) == null) { // If cache is empty make the binder call to get the state int state = profile.getConnectionStatus(mDevice); mProfileConnectionState.put(profile, state); diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java index fc0a444915c3..713e9675cccd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java @@ -428,15 +428,6 @@ public class TileUtils { if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) { int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW); remoteViews = new RemoteViews(applicationInfo.packageName, layoutId); - if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) { - String uriString = metaData.getString( - META_DATA_PREFERENCE_SUMMARY_URI); - String overrideSummary = getTextFromUri(context, uriString, providerMap, - META_DATA_PREFERENCE_SUMMARY); - if (overrideSummary != null) { - remoteViews.setTextViewText(android.R.id.summary, overrideSummary); - } - } } } } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { @@ -543,6 +534,30 @@ public class TileUtils { } } + public static void updateTileUsingSummaryUri(Context context, Tile tile) { + if (tile == null || tile.metaData == null || + !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) { + return; + } + + final Map<String, IContentProvider> providerMap = new HashMap<>(); + + final String uriString = tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI); + final Bundle bundle = getBundleFromUri(context, uriString, providerMap); + final String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY); + final String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE); + if (overrideSummary != null) { + tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary); + } + if (overrideTitle != null) { + tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle); + } + } + + private static String getString(Bundle bundle, String key) { + return bundle == null ? null : bundle.getString(key); + } + private static IContentProvider getProviderFromUri(Context context, Uri uri, Map<String, IContentProvider> providerMap) { if (uri == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TimestampedScoredNetwork.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TimestampedScoredNetwork.java index cb15a795fe9b..abc9b022b90b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/TimestampedScoredNetwork.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TimestampedScoredNetwork.java @@ -57,7 +57,7 @@ class TimestampedScoredNetwork implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mScore, flags); + mScore.writeToParcel(dest, flags); dest.writeLong(mUpdatedTimestampMillis); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 3e90435b21d7..88035e4fc600 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -22,10 +22,12 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.RuntimeEnvironment.application; @@ -64,9 +66,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.internal.ShadowExtractor; import java.util.ArrayList; import java.util.Collections; @@ -75,8 +74,7 @@ import java.util.Map; @RunWith(RobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, - sdk = TestConfig.SDK_VERSION, - shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class}) + sdk = TestConfig.SDK_VERSION) public class TileUtilsTest { @Mock @@ -421,24 +419,12 @@ public class TileUtilsTest { } @Test - public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary() + public void updateTileUsingSummaryUri_summaryUriSpecified_shouldOverrideRemoteViewSummary() throws RemoteException { - Intent intent = new Intent(); - Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); - List<Tile> outTiles = new ArrayList<>(); - List<ResolveInfo> info = new ArrayList<>(); - ResolveInfo resolveInfo = newInfo(true, null /* category */, null, - null, URI_GET_SUMMARY); - resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view", - R.layout.user_preference); - info.add(resolveInfo); - - when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) - .thenReturn(info); - // Mock the content provider interaction. Bundle bundle = new Bundle(); - bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text"); + String expectedSummary = "new summary text"; + bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, expectedSummary); when(mIContentProvider.call(anyString(), eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY), any())).thenReturn(bundle); @@ -447,57 +433,12 @@ public class TileUtilsTest { when(mContentResolver.acquireUnstableProvider(any(Uri.class))) .thenReturn(mIContentProvider); - TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, - null /* defaultCategory */, outTiles, false /* usePriority */, - false /* checkCategory */, true /* forceTintExternalIcon */); - - assertThat(outTiles.size()).isEqualTo(1); - Tile tile = outTiles.get(0); - assertThat(tile.remoteViews).isNotNull(); - assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference); - // Make sure the summary TextView got a new text string. - TileUtilsShadowRemoteViews shadowRemoteViews = - (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews); - assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary); - assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text"); - } - - @Test - public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary() - throws RemoteException { - Intent intent = new Intent(); - Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); - List<Tile> outTiles = new ArrayList<>(); - List<ResolveInfo> info = new ArrayList<>(); - ResolveInfo resolveInfo = newInfo(true, null /* category */, null, - null, URI_GET_SUMMARY); - resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view", - R.layout.user_preference); - info.add(resolveInfo); - - when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) - .thenReturn(info); - - // Mock the content provider interaction. - Bundle bundle = new Bundle(); - bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text"); - when(mIContentProvider.call(anyString(), - eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY), - any())).thenReturn(bundle); - - TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, - null /* defaultCategory */, outTiles, false /* usePriority */, - false /* checkCategory */, true /* forceTintExternalIcon */); - - assertThat(outTiles.size()).isEqualTo(1); - Tile tile = outTiles.get(0); - assertThat(tile.remoteViews).isNotNull(); - assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference); - // Make sure the summary TextView didn't get any text view updates. - TileUtilsShadowRemoteViews shadowRemoteViews = - (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews); - assertThat(shadowRemoteViews.overrideViewId).isNull(); - assertThat(shadowRemoteViews.overrideText).isNull(); + Tile tile = new Tile(); + tile.metaData = new Bundle(); + tile.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, URI_GET_SUMMARY); + tile.remoteViews = mock(RemoteViews.class); + TileUtils.updateTileUsingSummaryUri(mContext, tile); + verify(tile.remoteViews, times(1)).setTextViewText(anyInt(), eq(expectedSummary)); } public static ResolveInfo newInfo(boolean systemApp, String category) { @@ -558,16 +499,4 @@ public class TileUtilsTest { } } - @Implements(RemoteViews.class) - public static class TileUtilsShadowRemoteViews { - - private Integer overrideViewId; - private CharSequence overrideText; - - @Implementation - public void setTextViewText(int viewId, CharSequence text) { - overrideViewId = viewId; - overrideText = text; - } - } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java new file mode 100644 index 000000000000..b2d9d67bd104 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.settingslib.wifi; + +import static com.google.common.truth.Truth.assertThat; + +import android.net.NetworkKey; +import android.net.ScoredNetwork; +import android.net.WifiKey; +import android.os.Parcel; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import java.util.Date; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class TimestampedScoredNetworkTest { + private TimestampedScoredNetwork impl; + + private ScoredNetwork createTestScoredNetwork(String ssid) { + return new ScoredNetwork( + new NetworkKey(new WifiKey("\"" + ssid + "\"", "00:00:00:00:00:00")), null); + } + + @Before + public void setUp() { + impl = new TimestampedScoredNetwork(createTestScoredNetwork("test"), + 0 /* updatedTimestampMillis */); + } + + @Test + public void testUpdate() { + long time = new Date().getTime(); + ScoredNetwork updated = createTestScoredNetwork("updated"); + impl.update(updated, time); + + assertThat(impl.getScore()).isEqualTo(updated); + assertThat(impl.getUpdatedTimestampMillis()).isEqualTo(time); + } + + @Test + public void testParcel() { + Parcel parcel = Parcel.obtain(); + impl.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + TimestampedScoredNetwork fromParcel = TimestampedScoredNetwork.CREATOR.createFromParcel(parcel); + + assertThat(fromParcel.getScore()).isEqualTo(impl.getScore()); + assertThat(fromParcel.getUpdatedTimestampMillis()).isEqualTo(impl.getUpdatedTimestampMillis()); + } +} diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 75e919990f01..87971cb41f96 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -318,15 +318,20 @@ android:exported="false"> </activity> + <!-- Springboard for launching the share activity --> + <receiver android:name=".screenshot.GlobalScreenshot$ShareReceiver" + android:process=":screenshot" + android:exported="false" /> + <!-- Callback for dismissing screenshot notification after a share target is picked --> <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver" - android:process=":screenshot" - android:exported="false" /> + android:process=":screenshot" + android:exported="false" /> <!-- Callback for deleting screenshot notification --> <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver" - android:process=":screenshot" - android:exported="false" /> + android:process=":screenshot" + android:exported="false" /> <!-- started from UsbDeviceSettingsManager --> <activity android:name=".usb.UsbConfirmActivity" diff --git a/packages/SystemUI/res/color/qs_detail_progress_track.xml b/packages/SystemUI/res/color/qs_detail_progress_track.xml index c56382e6047f..d86119fc01fe 100644 --- a/packages/SystemUI/res/color/qs_detail_progress_track.xml +++ b/packages/SystemUI/res/color/qs_detail_progress_track.xml @@ -16,5 +16,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- I really don't want to define this, but the View that uses this asset uses both the light and dark accent colors. --> - <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_light" /> + <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_dark" /> </selector> diff --git a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml index 69869fe9c9be..5dcd9f78301b 100644 --- a/packages/SystemUI/res/drawable/ic_qs_no_sim.xml +++ b/packages/SystemUI/res/drawable/ic_qs_no_sim.xml @@ -1,5 +1,5 @@ <!-- -Copyright (C) 2014 The Android Open Source Project +Copyright (C) 2017 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. @@ -16,10 +16,14 @@ Copyright (C) 2014 The Android Open Source Project <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp" android:height="32dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportWidth="18.4" + android:viewportHeight="18.4"> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/> + <group + android:translateX="0.7" + android:translateY="1.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M13.91,11.84L5.14,3.08l1.81,-1.81h5.41c0.85,0 1.54,0.69 1.54,1.54l0.01,9.03zM15.06,14.95L2.54,2.44c-0.28,-0.28 -0.71,-0.28 -0.99,0s-0.28,0.71 0,0.98l1.53,1.53v8.67c0,0.85 0.69,1.54 1.54,1.54h7.74c0.27,0 0.52,-0.07 0.74,-0.2l0.96,0.96c0.28,0.28 0.71,0.28 0.99,0 0.28,-0.26 0.28,-0.69 0.01,-0.97z"/> + </group> </vector> diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml index dde2db2f484f..2f16516a6fea 100644 --- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml +++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml @@ -30,7 +30,8 @@ <TextView android:id="@+id/user_name" android:layout_width="@dimen/car_fullscreen_user_pod_width" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_above_text" + android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_name_top" + android:layout_marginBottom="@dimen/car_fullscreen_user_pod_margin_name_bottom" android:textSize="@dimen/car_fullscreen_user_pod_text_size" android:textColor="@color/qs_user_detail_name" android:ellipsize="end" diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/SystemUI/res/layout/car_qs_footer.xml index 96f34fca58f5..044090be9f5d 100644 --- a/packages/SystemUI/res/layout/car_qs_footer.xml +++ b/packages/SystemUI/res/layout/car_qs_footer.xml @@ -18,23 +18,24 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/qs_footer" android:layout_width="match_parent" - android:layout_height="@dimen/qs_footer_height" + android:layout_height="@dimen/car_qs_footer_height" android:baselineAligned="false" android:clickable="false" android:clipChildren="false" android:clipToPadding="false" - android:paddingBottom="16dp" - android:paddingTop="16dp" - android:paddingEnd="32dp" - android:paddingStart="32dp" + android:paddingBottom="@dimen/car_qs_footer_padding_bottom" + android:paddingTop="@dimen/car_qs_footer_padding_top" + android:paddingEnd="@dimen/car_qs_footer_padding_end" + android:paddingStart="@dimen/car_qs_footer_padding_start" android:gravity="center_vertical"> <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="@dimen/car_qs_footer_icon_width" + android:layout_height="@dimen/car_qs_footer_icon_height" + android:layout_marginRight="@dimen/car_qs_footer_user_switch_margin_right" android:background="@drawable/ripple_drawable" android:focusable="true"> @@ -43,15 +44,24 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" - android:scaleType="centerInside"/> + android:scaleType="fitCenter"/> </com.android.systemui.statusbar.phone.MultiUserSwitch> + <TextView android:id="@+id/user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="@dimen/car_qs_footer_user_name_text_size" + android:textColor="@color/car_qs_footer_user_name_color" + android:gravity="start|center_vertical" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/multi_user_switch" /> + <com.android.systemui.statusbar.phone.SettingsButton android:id="@+id/settings_button" android:layout_alignParentEnd="true" android:layout_centerVertical="true" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="@dimen/car_qs_footer_icon_width" + android:layout_height="@dimen/car_qs_footer_icon_height" android:background="@drawable/ripple_drawable" android:contentDescription="@string/accessibility_quick_settings_settings" android:scaleType="centerCrop" diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index bbd315ee8776..6df1657ba947 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -62,7 +62,9 @@ android:layout_height="wrap_content" android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info" android:layout_marginStart="2dp" - android:layout_marginEnd="2dp"/> + android:layout_marginEnd="2dp" + android:ellipsize="end" + android:maxLines="1"/> </LinearLayout> <!-- Channel Info Block --> diff --git a/packages/SystemUI/res/values-h600dp/dimens_car.xml b/packages/SystemUI/res/values-h600dp/dimens_car.xml new file mode 100644 index 000000000000..c3e62c8ec9c8 --- /dev/null +++ b/packages/SystemUI/res/values-h600dp/dimens_car.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2017, 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. +*/ +--> +<resources> + <dimen name="car_body2_size">32sp</dimen> <!-- B2 --> +</resources> diff --git a/packages/SystemUI/res/values-ldrtl/config.xml b/packages/SystemUI/res/values-ldrtl/config.xml index 40604c16be9b..884c95f57f51 100644 --- a/packages/SystemUI/res/values-ldrtl/config.xml +++ b/packages/SystemUI/res/values-ldrtl/config.xml @@ -17,5 +17,5 @@ <resources> <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP menu is shown with settings. --> - <string translatable="false" name="pip_settings_bounds">"778 54 1258 324"</string> + <string translatable="false" name="pip_settings_bounds">"778 756 1258 1026"</string> </resources> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 745d6015d0ff..a923f0b8c332 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -34,6 +34,7 @@ <attr name="recentItemLayout" format="reference" /> <!-- Style for the "Clear all" button. --> <attr name="clearAllStyle" format="reference" /> + <attr name="clearAllBackgroundColor" format="reference" /> </declare-styleable> <declare-styleable name="DeadZone"> <attr name="minSize" format="dimension" /> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index f72f3794e808..f244d88b8573 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -60,6 +60,11 @@ <!-- The background color for the freeform workspace. --> <color name="recents_freeform_workspace_bg_color">#33FFFFFF</color> + <!-- The background color for clear all button on light backgrounds if not transparent. --> + <color name="recents_clear_all_button_bg_light_color">#CCFFFFFF</color> + <!-- The background color for clear all button on dark backgrounds if not transparent. --> + <color name="recents_clear_all_button_bg_dark_color">#CC000000</color> + <color name="keyguard_affordance">#ffffffff</color> <!-- The color of the legacy notification background --> diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/SystemUI/res/values/colors_car.xml index 1b8c2fa68244..710b3e03ccf7 100644 --- a/packages/SystemUI/res/values/colors_car.xml +++ b/packages/SystemUI/res/values/colors_car.xml @@ -20,8 +20,12 @@ <color name="car_qs_background_primary">#263238</color> <!-- Blue Gray 900 --> <color name="car_user_switcher_progress_bgcolor">#00000000</color> <!-- Transparent --> <color name="car_user_switcher_progress_fgcolor">#80CBC4</color> <!-- Teal 200 --> - <color name="car_user_switcher_no_user_image_bgcolor">#FAFAFA</color> <!-- Grey 50 --> - <color name="car_user_switcher_no_user_image_fgcolor">#212121</color> <!-- Grey 900 --> - <color name="car_start_driving_background">#FAFAFA</color> <!-- Grey 50 --> - <color name="car_start_driving_text">#212121</color> <!-- Grey 900 --> + <color name="car_user_switcher_no_user_image_bgcolor">@color/car_grey_50</color> + <color name="car_user_switcher_no_user_image_fgcolor">@color/car_grey_900</color> + <color name="car_start_driving_background">@color/car_grey_50</color> + <color name="car_start_driving_text">@color/car_grey_900</color> + <color name="car_qs_footer_user_name_color">@color/car_grey_50</color> + + <color name="car_grey_50">#FAFAFA</color> + <color name="car_grey_900">#212121</color> </resources> diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml index 5f56c4ea6f2d..8853587dba47 100644 --- a/packages/SystemUI/res/values/dimens_car.xml +++ b/packages/SystemUI/res/values/dimens_car.xml @@ -18,7 +18,8 @@ <resources> <dimen name="car_margin">148dp</dimen> - <dimen name="car_fullscreen_user_pod_margin_above_text">24dp</dimen> + <dimen name="car_fullscreen_user_pod_margin_name_top">24dp</dimen> + <dimen name="car_fullscreen_user_pod_margin_name_bottom">64dp</dimen> <dimen name="car_fullscreen_user_pod_margin_between">24dp</dimen> <dimen name="car_fullscreen_user_pod_icon_text_size">96dp</dimen> <dimen name="car_fullscreen_user_pod_image_avatar_width">192dp</dimen> @@ -37,5 +38,17 @@ <dimen name="car_start_driving_corner_radius">16dp</dimen> <dimen name="car_start_driving_padding_side">30dp</dimen> <dimen name="car_start_driving_height">80dp</dimen> - <dimen name="car_start_driving_text_size">32sp</dimen> <!-- B2 --> + <dimen name="car_start_driving_text_size">@dimen/car_body2_size</dimen> + + <dimen name="car_qs_footer_height">112dp</dimen> + <dimen name="car_qs_footer_padding_bottom">16dp</dimen> + <dimen name="car_qs_footer_padding_top">16dp</dimen> + <dimen name="car_qs_footer_padding_end">46dp</dimen> + <dimen name="car_qs_footer_padding_start">46dp</dimen> + <dimen name="car_qs_footer_icon_width">56dp</dimen> + <dimen name="car_qs_footer_icon_height">56dp</dimen> + <dimen name="car_qs_footer_user_switch_margin_right">46dp</dimen> + <dimen name="car_qs_footer_user_name_text_size">@dimen/car_body2_size</dimen> + + <dimen name="car_body2_size">26sp</dimen> </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index b27deddb4dd0..2148c8047476 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -59,6 +59,7 @@ <item type="id" name="transformation_start_y_tag"/> <item type="id" name="transformation_start_scale_x_tag"/> <item type="id" name="transformation_start_scale_y_tag"/> + <item type="id" name="continuous_clipping_tag"/> <!-- Whether the icon is from a notification for which targetSdk < L --> <item type="id" name="icon_is_pre_L"/> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2199fffa4bbe..90c5977e2a67 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -34,11 +34,13 @@ <item name="android:windowShowWallpaper">true</item> <item name="android:windowDisablePreview">true</item> <item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item> + <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_dark_color</item> <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item> </style> <style name="RecentsTheme.Wallpaper.Light"> + <item name="clearAllBackgroundColor">@color/recents_clear_all_button_bg_light_color</item> <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item> <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item> </style> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index c596398cf804..d95402cdb5dc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -50,6 +50,7 @@ import android.media.AudioManager; import android.os.BatteryManager; import android.os.CancellationSignal; import android.os.Handler; +import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Message; import android.os.RemoteException; @@ -58,6 +59,8 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -67,8 +70,6 @@ import android.util.Log; import android.util.SparseBooleanArray; import android.util.SparseIntArray; -import com.google.android.collect.Lists; - import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.PhoneConstants; @@ -77,6 +78,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; +import com.google.android.collect.Lists; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -217,6 +220,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private UserManager mUserManager; private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED; private LockPatternUtils mLockPatternUtils; + private final IDreamManager mDreamManager; + private boolean mIsDreaming; /** * Short delay before restarting fingerprint authentication after a successful try @@ -458,6 +463,26 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { updateFingerprintListeningState(); } + /** + * @return a cached version of DreamManager.isDreaming() + */ + public boolean isDreaming() { + return mIsDreaming; + } + + /** + * If the device is dreaming, awakens the device + */ + public void awakenFromDream() { + if (mIsDreaming && mDreamManager != null) { + try { + mDreamManager.awaken(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to awaken from dream"); + } + } + } + private void onFingerprintAuthenticated(int userId) { Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated"); mUserFingerprintAuthenticated.put(userId, true); @@ -1037,11 +1062,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private void handleDreamingStateChanged(int dreamStart) { final int count = mCallbacks.size(); - boolean showingDream = dreamStart == 1; + mIsDreaming = dreamStart == 1; for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onDreamingStateChanged(showingDream); + cb.onDreamingStateChanged(mIsDreaming); } } } @@ -1146,6 +1171,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mLockPatternUtils = new LockPatternUtils(context); mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); + mDreamManager = IDreamManager.Stub.asInterface( + ServiceManager.getService(DreamService.DREAM_SERVICE)); + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); } @@ -1183,7 +1211,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean shouldListenForFingerprint() { return (mKeyguardIsVisible || !mDeviceInteractive || (mBouncer && !mKeyguardGoingAway) || mGoingToSleep || - shouldListenForFingerprintAssistant()) + shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming)) && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser()) && !mKeyguardGoingAway; } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index fd2447bd47b4..2b31967cea4f 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.statusbar.policy.IconLogger; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -150,7 +151,9 @@ public class BatteryMeterView extends LinearLayout implements public void onTuningChanged(String key, String newValue) { if (StatusBarIconController.ICON_BLACKLIST.equals(key)) { ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue); - setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE); + boolean hidden = icons.contains(mSlotBattery); + Dependency.get(IconLogger.class).onIconVisibility(mSlotBattery, !hidden); + setVisibility(hidden ? View.GONE : View.VISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 49253ec536cd..a9a915b23a0c 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -42,6 +42,7 @@ import com.android.systemui.power.PowerNotificationWarnings; import com.android.systemui.power.PowerUI; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -66,6 +67,8 @@ import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.FlashlightControllerImpl; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotControllerImpl; +import com.android.systemui.statusbar.policy.IconLogger; +import com.android.systemui.statusbar.policy.IconLoggerImpl; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; import com.android.systemui.statusbar.policy.LocationController; @@ -294,9 +297,13 @@ public class Dependency extends SystemUI { mProviders.put(UiOffloadThread.class, UiOffloadThread::new); - mProviders.put(PowerUI.WarningsUI.class, () -> new PowerNotificationWarnings(mContext)); + mProviders.put(IconLogger.class, () -> new IconLoggerImpl(mContext, + getDependency(BG_LOOPER), getDependency(MetricsLogger.class))); + + mProviders.put(LightBarController.class, () -> new LightBarController(mContext)); + // Put all dependencies above here so the factory can override them if it wants. SystemUIFactory.getInstance().injectDependencies(mProviders, mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 33d5617c0397..4cbbbd6c2e90 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -14,6 +14,8 @@ package com.android.systemui.globalactions; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import com.android.internal.R; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; @@ -310,7 +312,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) { mItems.add(getSettingsAction()); } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) { - mItems.add(getLockdownAction()); + if (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0) { + mItems.add(getLockdownAction()); + } } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) { mItems.add(getVoiceAssistAction()); } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) { @@ -575,7 +580,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn @Override public void onPress() { - new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL); + new LockPatternUtils(mContext) + .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, + UserHandle.USER_ALL); try { WindowManagerGlobal.getWindowManagerService().lockNow(null); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java index 3d8f9ffe79d7..5f2609380085 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java @@ -44,7 +44,10 @@ public class CellTileView extends SignalTileView { } protected void updateIcon(ImageView iv, State state) { - if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) { + if (!(state.icon instanceof SignalIcon)) { + super.updateIcon(iv, state); + return; + } else if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) { mSignalDrawable.setLevel(((SignalIcon) state.icon).getState()); iv.setImageDrawable(mSignalDrawable); iv.setTag(R.id.qs_icon_tag, state.icon); diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java index d42b87bcef28..142aab2626a6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java @@ -22,6 +22,7 @@ import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; +import android.widget.TextView; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -44,6 +45,7 @@ public class CarQSFooter extends RelativeLayout implements QSFooter, private UserInfoController mUserInfoController; private MultiUserSwitch mMultiUserSwitch; + private TextView mUserName; private ImageView mMultiUserAvatar; private UserGridView mUserGridView; @@ -56,6 +58,7 @@ public class CarQSFooter extends RelativeLayout implements QSFooter, super.onFinishInflate(); mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar); + mUserName = findViewById(R.id.user_name); mUserInfoController = Dependency.get(UserInfoController.class); @@ -89,6 +92,7 @@ public class CarQSFooter extends RelativeLayout implements QSFooter, @Override public void onUserInfoChanged(String name, Drawable picture, String userAccount) { mMultiUserAvatar.setImageDrawable(picture); + mUserName.setText(name); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 6c95a8013108..5a3081cd6664 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -18,6 +18,7 @@ package com.android.systemui.qs.customize; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; +import android.app.AlertDialog; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; @@ -35,6 +36,8 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; import android.widget.LinearLayout; import android.widget.Toolbar; import android.widget.Toolbar.OnMenuItemClickListener; @@ -48,7 +51,9 @@ import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSContainerImpl; import com.android.systemui.qs.QSDetailClipper; import com.android.systemui.qs.QSTileHost; +import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; +import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; @@ -67,6 +72,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private static final String EXTRA_QS_CUSTOMIZING = "qs_customizing"; private final QSDetailClipper mClipper; + private final LightBarController mLightBarController; private boolean isShown; private QSTileHost mHost; @@ -80,6 +86,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private int mX; private int mY; private boolean mOpening; + private boolean mIsShowingNavBackdrop; public QSCustomizer(Context context, AttributeSet attrs) { super(new ContextThemeWrapper(context, R.style.edit_theme), attrs); @@ -114,6 +121,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene DefaultItemAnimator animator = new DefaultItemAnimator(); animator.setMoveDuration(TileAdapter.MOVE_DURATION); mRecyclerView.setItemAnimator(animator); + mLightBarController = Dependency.get(LightBarController.class); updateNavBackDrop(getResources().getConfiguration()); } @@ -125,11 +133,16 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private void updateNavBackDrop(Configuration newConfig) { View navBackdrop = findViewById(R.id.nav_bar_background); + mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600 + || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE; if (navBackdrop != null) { - boolean shouldShow = newConfig.smallestScreenWidthDp >= 600 - || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE; - navBackdrop.setVisibility(shouldShow ? View.VISIBLE : View.GONE); + navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE); } + updateNavColors(); + } + + private void updateNavColors() { + mLightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown); } public void setHost(QSTileHost host) { @@ -161,6 +174,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene announceForAccessibility(mContext.getString( R.string.accessibility_desc_quick_settings_edit)); Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback); + updateNavColors(); } } @@ -176,6 +190,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mNotifQsContainer.setCustomizerAnimating(false); mNotifQsContainer.setCustomizerShowing(true); Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback); + updateNavColors(); } } @@ -201,6 +216,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene announceForAccessibility(mContext.getString( R.string.accessibility_desc_quick_settings)); Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback); + updateNavColors(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 017365f6c64d..176112baa2a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -31,6 +31,7 @@ import android.provider.Settings; import android.service.quicksettings.IQSTileService; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; +import android.text.format.DateUtils; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManagerGlobal; @@ -51,6 +52,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; public class CustomTile extends QSTileImpl<State> implements TileChangeListener { public static final String PREFIX = "custom("; + private static final long CUSTOM_STALE_TIMEOUT = DateUtils.HOUR_IN_MILLIS; + private static final boolean DEBUG = false; // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot. @@ -83,6 +86,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener mUser = ActivityManager.getCurrentUser(); } + @Override + protected long getStaleTimeout() { + return CUSTOM_STALE_TIMEOUT + DateUtils.MINUTE_IN_MILLIS * mHost.indexOf(getTileSpec()); + } + private void setTileIcon() { try { PackageManager pm = mContext.getPackageManager(); @@ -186,7 +194,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mListening == listening) return; mListening = listening; try { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 672f2c2df06e..576a447447b5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -17,12 +17,12 @@ package com.android.systemui.qs.tileimpl; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CONTEXT; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; -import android.R.attr; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -32,10 +32,12 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.service.quicksettings.Tile; +import android.text.format.DateUtils; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.Utils; @@ -45,6 +47,7 @@ import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; +import com.android.systemui.qs.PagedTileLayout.TilePage; import com.android.systemui.qs.QSHost; import java.util.ArrayList; @@ -60,14 +63,18 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { protected final String TAG = "Tile." + getClass().getSimpleName(); protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG); + private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS; + protected final QSHost mHost; protected final Context mContext; - protected final H mHandler = new H(Dependency.get(Dependency.BG_LOOPER)); + // @NonFinalForTesting + protected H mHandler = new H(Dependency.get(Dependency.BG_LOOPER)); protected final Handler mUiHandler = new Handler(Looper.getMainLooper()); private final ArraySet<Object> mListeners = new ArraySet<>(); private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final Object mStaleListener = new Object(); protected TState mState = newTileState(); private TState mTmpState = newTileState(); private boolean mAnnounceNextStateChange; @@ -93,6 +100,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { protected QSTileImpl(QSHost host) { mHost = host; mContext = host.getContext(); + handleStale(); // Tile was just created, must be stale. } /** @@ -104,6 +112,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { if (mListeners.add(listener) && mListeners.size() == 1) { if (DEBUG) Log.d(TAG, "setListening " + true); mHandler.obtainMessage(H.SET_LISTENING, 1, 0).sendToTarget(); + refreshState(); // Ensure we get at least one refresh after listening. } } else { if (mListeners.remove(listener) && mListeners.size() == 0) { @@ -113,6 +122,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { } } + protected long getStaleTimeout() { + return DEFAULT_STALE_TIMEOUT; + } + + @VisibleForTesting + protected void handleStale() { + setListening(mStaleListener, true); + } + public String getTileSpec() { return mTileSpec; } @@ -180,9 +198,19 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0); } return logMaker.setSubtype(getMetricsCategory()) + .addTaggedData(FIELD_CONTEXT, isFullQs()) .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec)); } + private int isFullQs() { + for (Object listener : mListeners) { + if (TilePage.class.equals(listener.getClass())) { + return 1; + } + } + return 0; + } + public void showDetail(boolean show) { mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget(); } @@ -261,6 +289,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { if (changed) { handleStateChanged(); } + mHandler.removeMessages(H.STALE); + mHandler.sendEmptyMessageDelayed(H.STALE, getStaleTimeout()); + setListening(mStaleListener, false); } private void handleStateChanged() { @@ -314,11 +345,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { handleRefreshState(null); } - protected abstract void setListening(boolean listening); + protected abstract void handleSetListening(boolean listening); protected void handleDestroy() { if (mListeners.size() != 0) { - setListening(false); + handleSetListening(false); } mCallbacks.clear(); } @@ -368,8 +399,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { private static final int REMOVE_CALLBACKS = 12; private static final int REMOVE_CALLBACK = 13; private static final int SET_LISTENING = 14; + private static final int STALE = 15; - private H(Looper looper) { + @VisibleForTesting + protected H(Looper looper) { super(looper); } @@ -424,8 +457,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { name = "handleClearState"; handleClearState(); } else if (msg.what == SET_LISTENING) { - name = "setListening"; - setListening(msg.arg1 != 0); + name = "handleSetListening"; + handleSetListening(msg.arg1 != 0); + } else if (msg.what == STALE) { + name = "handleStale"; + handleStale(); } else { throw new IllegalArgumentException("Unknown msg: " + msg.what); } @@ -439,15 +475,22 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { public static class DrawableIcon extends Icon { protected final Drawable mDrawable; + protected final Drawable mInvisibleDrawable; public DrawableIcon(Drawable drawable) { mDrawable = drawable; + mInvisibleDrawable = drawable.getConstantState().newDrawable(); } @Override public Drawable getDrawable(Context context) { return mDrawable; } + + @Override + public Drawable getInvisibleDrawable(Context context) { + return mInvisibleDrawable; + } } public static class DrawableIconWithRes extends DrawableIcon { @@ -503,7 +546,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { } } - protected class AnimationIcon extends ResourceIcon { + protected static class AnimationIcon extends ResourceIcon { private final int mAnimatedResId; public AnimationIcon(int resId, int staticResId) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 2e7012e95e29..bef1aff571f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -110,7 +110,7 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { } } - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mListening == listening) return; mListening = listening; if (listening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 3f419a81d36e..95504edf2e10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -55,7 +55,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mBatteryController.addCallback(this); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index bc6233d45a67..774f0b3dfbb4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -76,7 +76,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mController.addCallback(mCallback); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 2fc9fc7b09b0..fb396b9ddb07 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -91,9 +91,9 @@ public class CastTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mController == null) return; - if (DEBUG) Log.d(TAG, "setListening " + listening); + if (DEBUG) Log.d(TAG, "handleSetListening " + listening); if (listening) { mController.addCallback(mCallback); mKeyguard.addCallback(mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 0e0f949ce42f..2e389ba151bd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -92,7 +92,7 @@ public class CellularTile extends QSTileImpl<SignalState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mController.addCallback(mSignalCallback); } else { @@ -112,6 +112,9 @@ public class CellularTile extends QSTileImpl<SignalState> { @Override protected void handleClick() { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return; + } if (mDataController.isMobileDataEnabled()) { if (mKeyguardMonitor.isSecure() && !mKeyguardMonitor.canSkipBouncer()) { mActivityStarter.postQSRunnableDismissingKeyguard(this::showDisableDialog); @@ -183,9 +186,14 @@ public class CellularTile extends QSTileImpl<SignalState> { state.value = mDataController.isMobileDataSupported() && mDataController.isMobileDataEnabled(); - state.icon = new SignalIcon(cb.mobileSignalIconId); - if (cb.airplaneModeEnabled) { - state.state = Tile.STATE_INACTIVE; + if (cb.noSim) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim); + } else { + state.icon = new SignalIcon(cb.mobileSignalIconId); + } + + if (cb.airplaneModeEnabled | cb.noSim) { + state.state = Tile.STATE_UNAVAILABLE; } else { state.state = Tile.STATE_ACTIVE; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 40fe4844955b..b93f1c208c11 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -63,7 +63,7 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { mSetting.setListening(listening); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index 8b62beb861ba..a1026967633f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -45,7 +45,7 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mDataSaverController.addCallback(this); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 5938749d14d2..9e265e2295c3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -232,7 +232,7 @@ public class DndTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mListening == listening) return; mListening = listening; if (mListening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index e6ac90872403..f2ead1cbca94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -54,7 +54,7 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mFlashlightController.addCallback(this); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index c17573d9189a..910b6b174062 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -74,7 +74,7 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mListening == listening) return; mListening = listening; if (listening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java index 00cfbfa1eac7..4f4004c6efdc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java @@ -76,7 +76,7 @@ public class IntentTile extends QSTileImpl<State> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 5e663342905e..c35f5917d6cc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -55,7 +55,7 @@ public class LocationTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mController.addCallback(mCallback); mKeyguard.addCallback(mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index 6500740761ce..b3ff4e5b890c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -51,7 +51,7 @@ public class NfcTile extends QSTileImpl<BooleanState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { mListening = listening; if (mListening) { mContext.registerReceiver(mNfcReceiver, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 2a127695a53e..4c2036141daf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -95,7 +95,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> } @Override - protected void setListening(boolean listening) { + protected void handleSetListening(boolean listening) { mIsListening = listening; if (listening) { mController.setListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index fb937bd09bc2..1e00894483ac 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -62,7 +62,7 @@ public class RotationLockTile extends QSTileImpl<BooleanState> { return new BooleanState(); } - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (mController == null) return; if (listening) { mController.addCallback(mCallback); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java index d6043f4fff1f..bde1c98d023c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java @@ -69,7 +69,7 @@ public class UserTile extends QSTileImpl<State> implements UserInfoController.On } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mUserInfoController.addCallback(this); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 136cf216f653..33b15121b47e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -75,7 +75,7 @@ public class WifiTile extends QSTileImpl<SignalState> { } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mController.addCallback(mSignalCallback); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index 38821f6aa081..5f7d6fb41311 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -48,7 +48,7 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements } @Override - public void setListening(boolean listening) { + public void handleSetListening(boolean listening) { if (listening) { mProfileController.addCallback(this); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index f8448661d799..f5455568f799 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -65,6 +65,7 @@ import com.android.systemui.recents.events.activity.IterateRecentsEvent; import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; +import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; import com.android.systemui.recents.events.component.ActivityUnpinnedEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; @@ -119,6 +120,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD private boolean mFinishedOnStartup; private boolean mIgnoreAltTabRelease; private boolean mIsVisible; + private boolean mRecentsStartRequested; private Configuration mLastConfig; // Top level views @@ -416,6 +418,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD launchState.launchedFromHome = false; onEnterAnimationComplete(); } + mRecentsStartRequested = false; } @Override @@ -449,7 +452,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD * Reloads the stack views upon launching Recents. */ private void reloadStackView() { - // If the Recents component has preloaded a load plan, then use that to prevent // reconstructing the task stack RecentsTaskLoader loader = Recents.getTaskLoader(); @@ -572,7 +574,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY); Recents.getTaskLoader().getHighResThumbnailLoader().setVisible(false); - if (!isChangingConfigurations()) { + // When recents starts again before onStop, do not reset launch flags so entrance animation + // can run + if (!isChangingConfigurations() && !mRecentsStartRequested) { // Workaround for b/22542869, if the RecentsActivity is started again, but without going // through SystemUI, we need to reset the config launch flags to ensure that we do not // wait on the system to send a signal that was never queued. @@ -718,6 +722,10 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE); } + public final void onBusEvent(RecentsActivityStartingEvent event) { + mRecentsStartRequested = true; + } + public final void onBusEvent(UserInteractionEvent event) { // Stop the fast-toggle dozer mIterateTrigger.stopDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 71f06cbf6e64..c44cd7287625 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -205,6 +205,10 @@ public class RecentsView extends FrameLayout { mStackButtonShadowDistance.x, mStackButtonShadowDistance.y, mStackButtonShadowColor); } + if (Recents.getConfiguration().isLowRamDevice) { + int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor); + mStackActionButton.setBackgroundColor(bgColor); + } } // Let's also require dark status and nav bars if the text is dark @@ -955,8 +959,8 @@ public class RecentsView extends FrameLayout { int left, top; if (Recents.getConfiguration().isLowRamDevice) { Rect windowRect = Recents.getSystemServices().getWindowRect(); - left = (windowRect.width() - mSystemInsets.left - mSystemInsets.right - - mStackActionButton.getMeasuredWidth()) / 2; + int spaceLeft = windowRect.width() - mSystemInsets.left - mSystemInsets.right; + left = (spaceLeft - mStackActionButton.getMeasuredWidth()) / 2 + mSystemInsets.left; top = windowRect.height() - (mStackActionButton.getMeasuredHeight() + mSystemInsets.bottom + mStackActionButton.getPaddingBottom() / 2); } else { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index a633a3ebda07..8899e307bb16 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -2396,10 +2396,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } private void updateStackActionButtonVisibility() { + if (Recents.getConfiguration().isLowRamDevice) { + return; + } + // Always show the button in grid layout. if (useGridLayout() || (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD && - mStack.getTaskCount() > 0 && !Recents.getConfiguration().isLowRamDevice)) { + mStack.getTaskCount() > 0)) { EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */)); } else { EventBus.getDefault().send(new HideStackActionButtonEvent()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java index 52fa7b5c7186..17e6b9e3c195 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/lowram/TaskStackLowRamLayoutAlgorithm.java @@ -126,7 +126,6 @@ public class TaskStackLowRamLayoutAlgorithm { return transformOut; } boolean visible = true; - int x = mPaddingLeftRight; int y; if (taskCount > 1) { y = getTaskTopFromIndex(taskIndex) - percentageToScroll(stackScroll); @@ -255,7 +254,7 @@ public class TaskStackLowRamLayoutAlgorithm { transformOut.dimAlpha = 0f; transformOut.viewOutlineAlpha = 1f; transformOut.rect.set(getTaskRect()); - transformOut.rect.offset(mPaddingLeftRight, y); + transformOut.rect.offset(mPaddingLeftRight + mSystemInsets.left, y); Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale); transformOut.visible = visible; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index a35310fc7d59..991c3c83cbc1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -16,11 +16,16 @@ package com.android.systemui.screenshot; +import static com.android.systemui.screenshot.GlobalScreenshot.SHARING_INTENT; +import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.admin.DevicePolicyManager; import android.app.Notification; import android.app.Notification.BigPictureStyle; @@ -48,6 +53,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; import android.util.DisplayMetrics; @@ -277,14 +283,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - // Create a share action for the notification - PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - Intent chooserIntent = Intent.createChooser(sharingIntent, null, - chooseAction.getIntentSender()) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); - PendingIntent shareAction = PendingIntent.getActivity(context, 0, chooserIntent, + // Create a share action for the notification. Note, we proxy the call to ShareReceiver + // because RemoteViews currently forces an activity options on the PendingIntent being + // launched, and since we don't want to trigger the share sheet in this case, we will + // start the chooser activitiy directly in ShareReceiver. + PendingIntent shareAction = PendingIntent.getBroadcast(context, 0, + new Intent(context, GlobalScreenshot.ShareReceiver.class) + .putExtra(SHARING_INTENT, sharingIntent), PendingIntent.FLAG_CANCEL_CURRENT); Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( R.drawable.ic_screenshot_share, @@ -292,7 +297,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mNotificationBuilder.addAction(shareActionBuilder.build()); // Create a delete action for the notification - PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, + PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); @@ -403,6 +408,7 @@ class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> { class GlobalScreenshot { static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; + static final String SHARING_INTENT = "android:screenshot_sharing_intent"; private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; private static final int SCREENSHOT_DROP_IN_DURATION = 430; @@ -897,6 +903,30 @@ class GlobalScreenshot { } /** + * Receiver to proxy the share intent. + */ + public static class ShareReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + try { + ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_SCREENSHOT); + } catch (RemoteException e) { + } + + Intent sharingIntent = intent.getParcelableExtra(SHARING_INTENT); + PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0, + new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Intent chooserIntent = Intent.createChooser(sharingIntent, null, + chooseAction.getIntentSender()) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setDisallowEnterPictureInPictureWhileLaunching(true); + context.startActivityAsUser(chooserIntent, opts.toBundle(), UserHandle.CURRENT); + } + } + + /** * Removes the notification for a screenshot after a share target is chosen. */ public static class TargetChosenReceiver extends BroadcastReceiver { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 1ffb3b5387a3..3b23a0c0db89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -126,7 +126,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE); if (info != null) { - mAppUid = info.uid; + mAppUid = sbn.getUid(); mAppName = String.valueOf(pm.getApplicationLabel(info)); pkgicon = pm.getApplicationIcon(info); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 5eefe9ae0736..5557dde7a5d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -22,10 +22,12 @@ import static com.android.systemui.statusbar.phone.NotificationIconContainer.OVE import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.os.SystemProperties; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; import com.android.systemui.Interpolators; @@ -53,6 +55,7 @@ public class NotificationShelf extends ActivatableNotificationView implements SystemProperties.getBoolean("debug.icon_opening_animations", true); private static final boolean ICON_ANMATIONS_WHILE_SCROLLING = SystemProperties.getBoolean("debug.icon_scroll_animations", true); + private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag; private ViewInvertHelper mViewInvertHelper; private boolean mDark; private NotificationIconContainer mShelfIcons; @@ -305,6 +308,16 @@ public class NotificationShelf extends ActivatableNotificationView implements mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); + for (int i = 0; i < mHostLayout.getChildCount(); i++) { + View child = mHostLayout.getChildAt(i); + if (!(child instanceof ExpandableNotificationRow) + || child.getVisibility() == GONE) { + continue; + } + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + updateIconClipAmount(row); + updateContinuousClipping(row); + } boolean hideBackground = numViewsInShelf < 1.0f; setHideBackground(hideBackground || backgroundForceHidden); if (mNotGoneIndex == -1) { @@ -312,6 +325,43 @@ public class NotificationShelf extends ActivatableNotificationView implements } } + private void updateIconClipAmount(ExpandableNotificationRow row) { + float maxTop = row.getTranslationY(); + StatusBarIconView icon = row.getEntry().expandedIcon; + float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY(); + if (shelfIconPosition < maxTop) { + int top = (int) (maxTop - shelfIconPosition); + Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight())); + icon.setClipBounds(clipRect); + } else { + icon.setClipBounds(null); + } + } + + private void updateContinuousClipping(final ExpandableNotificationRow row) { + StatusBarIconView icon = row.getEntry().expandedIcon; + boolean needsContinuousClipping = ViewState.isAnimatingY(icon); + boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null; + if (needsContinuousClipping && !isContinuousClipping) { + ViewTreeObserver.OnPreDrawListener predrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + boolean animatingY = ViewState.isAnimatingY(icon); + if (!animatingY || !icon.isAttachedToWindow()) { + icon.getViewTreeObserver().removeOnPreDrawListener(this); + icon.setTag(TAG_CONTINUOUS_CLIPPING, null); + return true; + } + updateIconClipAmount(row); + return true; + } + }; + icon.getViewTreeObserver().addOnPreDrawListener(predrawListener); + icon.setTag(TAG_CONTINUOUS_CLIPPING, predrawListener); + } + } + private void updateNotificationClipHeight(ExpandableNotificationRow row, float notificationClipEnd) { float viewEnd = row.getTranslationY() + row.getActualHeight(); @@ -330,6 +380,15 @@ public class NotificationShelf extends ActivatableNotificationView implements } } + @Override + public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, + int outlineTranslation) { + if (!mHasItemsInStableShelf) { + shadowIntensity = 0.0f; + } + super.setFakeShadowIntensity(shadowIntensity, outlineAlpha, shadowYEnd, outlineTranslation); + } + /** * @return the icon amount how much this notification is in the shelf; */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 25f3e25f2051..759d2cf2f8bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.SignalDrawable; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.statusbar.policy.IconLogger; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkControllerImpl; @@ -65,6 +66,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController private static final String SLOT_MOBILE = "mobile"; private static final String SLOT_WIFI = "wifi"; private static final String SLOT_ETHERNET = "ethernet"; + private static final String SLOT_VPN = "vpn"; private final NetworkController mNetworkController; private final SecurityController mSecurityController; @@ -117,6 +119,8 @@ public class SignalClusterView extends LinearLayout implements NetworkController private boolean mActivityEnabled; private boolean mForceBlockWifi; + private final IconLogger mIconLogger = Dependency.get(IconLogger.class); + public SignalClusterView(Context context) { this(context, null); } @@ -447,14 +451,15 @@ public class SignalClusterView extends LinearLayout implements NetworkController private void apply() { if (mWifiGroup == null) return; - mVpn.setVisibility(mVpnVisible ? View.VISIBLE : View.GONE); if (mVpnVisible) { if (mLastVpnIconId != mVpnIconId) { setIconForView(mVpn, mVpnIconId); mLastVpnIconId = mVpnIconId; } + mIconLogger.onIconShown(SLOT_VPN); mVpn.setVisibility(View.VISIBLE); } else { + mIconLogger.onIconHidden(SLOT_VPN); mVpn.setVisibility(View.GONE); } if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE")); @@ -466,8 +471,10 @@ public class SignalClusterView extends LinearLayout implements NetworkController mLastEthernetIconId = mEthernetIconId; } mEthernetGroup.setContentDescription(mEthernetDescription); + mIconLogger.onIconShown(SLOT_ETHERNET); mEthernetGroup.setVisibility(View.VISIBLE); } else { + mIconLogger.onIconHidden(SLOT_ETHERNET); mEthernetGroup.setVisibility(View.GONE); } @@ -481,9 +488,11 @@ public class SignalClusterView extends LinearLayout implements NetworkController setIconForView(mWifiDark, mWifiStrengthId); mLastWifiStrengthId = mWifiStrengthId; } + mIconLogger.onIconShown(SLOT_WIFI); mWifiGroup.setContentDescription(mWifiDescription); mWifiGroup.setVisibility(View.VISIBLE); } else { + mIconLogger.onIconHidden(SLOT_WIFI); mWifiGroup.setVisibility(View.GONE); } @@ -505,6 +514,11 @@ public class SignalClusterView extends LinearLayout implements NetworkController } } } + if (anyMobileVisible) { + mIconLogger.onIconShown(SLOT_MOBILE); + } else { + mIconLogger.onIconHidden(SLOT_MOBILE); + } if (mIsAirplaneMode) { if (mLastAirplaneIconId != mAirplaneIconId) { @@ -512,8 +526,10 @@ public class SignalClusterView extends LinearLayout implements NetworkController mLastAirplaneIconId = mAirplaneIconId; } mAirplane.setContentDescription(mAirplaneContentDescription); + mIconLogger.onIconShown(SLOT_AIRPLANE); mAirplane.setVisibility(View.VISIBLE); } else { + mIconLogger.onIconHidden(SLOT_AIRPLANE); mAirplane.setVisibility(View.GONE); } @@ -529,7 +545,13 @@ public class SignalClusterView extends LinearLayout implements NetworkController mWifiSignalSpacer.setVisibility(View.GONE); } - mNoSimsCombo.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE); + if (mNoSimsVisible) { + mIconLogger.onIconShown(SLOT_MOBILE); + mNoSimsCombo.setVisibility(View.VISIBLE); + } else { + mIconLogger.onIconHidden(SLOT_MOBILE); + mNoSimsCombo.setVisibility(View.GONE); + } boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode || anyMobileVisible || mVpnVisible || mEthernetVisible; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java index fcc982ea3fb0..b95b8a392c86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java @@ -114,7 +114,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mIcon = mView.findViewById(com.android.internal.R.id.icon); mHeaderText = mView.findViewById(com.android.internal.R.id.header_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); - mExpandButton.setLabeledBy(mRow); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mColor = resolveColor(mExpandButton); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index f379a4636623..1f44abea755a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -41,8 +41,6 @@ public class BarTransitions { private static final boolean DEBUG = false; private static final boolean DEBUG_COLORS = false; - public static final boolean HIGH_END = ActivityManager.isHighEndGfx(); - public static final int MODE_OPAQUE = 0; public static final int MODE_SEMI_TRANSPARENT = 1; public static final int MODE_TRANSLUCENT = 2; @@ -66,9 +64,7 @@ public class BarTransitions { mTag = "BarTransitions." + view.getClass().getSimpleName(); mView = view; mBarBackground = new BarBackgroundDrawable(mView.getContext(), gradientResourceId); - if (HIGH_END) { - mView.setBackground(mBarBackground); - } + mView.setBackground(mBarBackground); } public int getMode() { @@ -89,7 +85,7 @@ public class BarTransitions { public boolean isAlwaysOpaque() { // Low-end devices do not support translucent modes, fallback to opaque - return !HIGH_END || mAlwaysOpaque; + return mAlwaysOpaque; } public void transitionTo(int mode, boolean animate) { @@ -109,9 +105,7 @@ public class BarTransitions { } protected void onTransition(int oldMode, int newMode, boolean animate) { - if (HIGH_END) { - applyModeBackground(oldMode, newMode, animate); - } + applyModeBackground(oldMode, newMode, animate); } protected void applyModeBackground(int oldMode, int newMode, boolean animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 8c923cbcde0b..2c3f452e8274 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -161,10 +161,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue showNotificationIconArea(animate); } } - if (!BarTransitions.HIGH_END) { - int mask = DISABLE_NOTIFICATION_ICONS | DISABLE_SYSTEM_INFO; - getView().setVisibility((mDisabled1 & mask) == mask ? View.GONE : View.VISIBLE); - } } protected int adjustDisableFlags(int state) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java index 316d229e4f1a..00cb5329d2e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java @@ -85,6 +85,11 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { public static final int MODE_DISMISS_BOUNCER = 6; /** + * Mode in which fingerprint wakes and unlocks the device from a dream. + */ + public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 7; + + /** * How much faster we collapse the lockscreen when authenticating with fingerprint. */ private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.1f; @@ -230,16 +235,19 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { } Trace.endSection(); break; + case MODE_WAKE_AND_UNLOCK_FROM_DREAM: case MODE_WAKE_AND_UNLOCK_PULSING: case MODE_WAKE_AND_UNLOCK: if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) { Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING"); mStatusBar.updateMediaMetaData(false /* metaDataChanged */, true /* allowEnterAnimation */); - } else { + } else if (mMode == MODE_WAKE_AND_UNLOCK){ Trace.beginSection("MODE_WAKE_AND_UNLOCK"); - mDozeScrimController.abortDoze(); + } else { + Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM"); + mUpdateMonitor.awakenFromDream(); } mStatusBarWindowManager.setStatusBarFocusable(false); mKeyguardViewMediator.onWakeAndUnlocking(); @@ -299,6 +307,7 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { private int calculateMode() { boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed(); + boolean deviceDreaming = mUpdateMonitor.isDreaming(); if (!mUpdateMonitor.isDeviceInteractive()) { if (!mStatusBarKeyguardViewManager.isShowing()) { @@ -311,6 +320,9 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { return MODE_SHOW_BOUNCER; } } + if (unlockingAllowed && deviceDreaming) { + return MODE_WAKE_AND_UNLOCK_FROM_DREAM; + } if (mStatusBarKeyguardViewManager.isShowing()) { if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) { return MODE_DISMISS_BOUNCER; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index fd95cc4adc1d..165ed78996b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -27,6 +27,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import com.android.internal.widget.LockPatternUtils; @@ -247,12 +248,16 @@ public class KeyguardBouncer { removeView(); mHandler.removeCallbacks(mRemoveViewRunnable); mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); - mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view); + mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view); mKeyguardView.setLockPatternUtils(mLockPatternUtils); mKeyguardView.setViewMediatorCallback(mCallback); mContainer.addView(mRoot, mContainer.getChildCount()); mRoot.setVisibility(View.INVISIBLE); - mRoot.dispatchApplyWindowInsets(mRoot.getRootWindowInsets()); + + final WindowInsets rootInsets = mRoot.getRootWindowInsets(); + if (rootInsets != null) { + mRoot.dispatchApplyWindowInsets(rootInsets); + } } protected void removeView() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 917a56fbc99d..533771a31d0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -72,6 +72,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private final Rect mLastFullscreenBounds = new Rect(); private final Rect mLastDockedBounds = new Rect(); + private boolean mQsCustomizing; public LightBarController(Context ctx) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); @@ -129,7 +130,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mHasLightNavigationBar = isLight(vis, navigationBarMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); mNavigationLight = mHasLightNavigationBar - && (mScrimAlphaBelowThreshold || !mInvertLightNavBarWithScrim); + && (mScrimAlphaBelowThreshold || !mInvertLightNavBarWithScrim) + && !mQsCustomizing; if (mNavigationLight != last) { updateNavigation(); } @@ -146,6 +148,12 @@ public class LightBarController implements BatteryController.BatteryStateChangeC mLastNavigationBarMode); } + public void setQsCustomizing(boolean customizing) { + if (mQsCustomizing == customizing) return; + mQsCustomizing = customizing; + reevaluate(); + } + public void setScrimAlpha(float alpha) { mScrimAlpha = alpha; boolean belowThresholdBefore = mScrimAlphaBelowThreshold; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index d3a6280212cd..b0ac6ecae5bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -127,11 +127,6 @@ public class LightBarTransitionsController implements Dumpable, Callbacks { } public void setIconsDark(boolean dark, boolean animate) { - if (!BarTransitions.HIGH_END) { - setIconTintInternal(0.0f); - mNextDarkIntensity = 0.0f; - return; - } if (!animate) { setIconTintInternal(dark ? 1.0f : 0.0f); mNextDarkIntensity = dark ? 1.0f : 0.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java index 004a6043ff94..1452e0c7a0b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java @@ -87,7 +87,8 @@ public class NearestTouchFrame extends FrameLayout { if (mTouchingChild != null) { event.offsetLocation(mTouchingChild.getWidth() / 2 - event.getX(), mTouchingChild.getHeight() / 2 - event.getY()); - return mTouchingChild.dispatchTouchEvent(event); + return mTouchingChild.getVisibility() == VISIBLE + && mTouchingChild.dispatchTouchEvent(event); } } return super.onTouchEvent(event); 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 0575e21d6623..00ba1f293b8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -300,6 +300,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Should match the values in PhoneWindowManager public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; + static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot"; private static final String BANNER_ACTION_CANCEL = "com.android.systemui.statusbar.banner_action_cancel"; @@ -1124,7 +1125,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }); - mLightBarController = new LightBarController(context); + mLightBarController = Dependency.get(LightBarController.class); if (mNavigationBar != null) { mNavigationBar.setLightBarController(mLightBarController); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 70b92ade1bbc..68f8e065a429 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -20,12 +20,8 @@ import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import android.util.ArraySet; -import android.view.Gravity; -import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; @@ -38,6 +34,7 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.IconLogger; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -61,6 +58,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); private final ArraySet<String> mIconBlacklist = new ArraySet<>(); + private final IconLogger mIconLogger = Dependency.get(IconLogger.class); public StatusBarIconControllerImpl(Context context) { super(context.getResources().getStringArray( @@ -122,6 +120,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu int viewIndex = getViewIndex(index); boolean blocked = mIconBlacklist.contains(slot); + mIconLogger.onIconVisibility(getSlot(index), icon.visible); mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon)); } @@ -174,6 +173,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu if (getIcon(index) == null) { return; } + mIconLogger.onIconHidden(getSlot(index)); super.removeIcon(index); int viewIndex = getViewIndex(index); mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); @@ -196,6 +196,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private void handleSet(int index, StatusBarIcon icon) { int viewIndex = getViewIndex(index); + mIconLogger.onIconVisibility(getSlot(index), icon.visible); mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java index 1411a544c346..f9c2130e4e81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java @@ -159,6 +159,11 @@ public class UnlockMethodCache { public void onScreenTurnedOff() { update(false /* updateAlways */); } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + update(false /* updateAlways */); + } }; public boolean isTrustManaged() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index f0af77de6a22..4c92d01eae4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -24,7 +24,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Rect; import android.os.Bundle; @@ -193,8 +192,9 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C } private void updateClockVisibility() { - int visibility = (mClockVisibleByPolicy && mClockVisibleByUser) - ? View.VISIBLE : View.GONE; + boolean visible = mClockVisibleByPolicy && mClockVisibleByUser; + Dependency.get(IconLogger.class).onIconVisibility("clock", visible); + int visibility = visible ? View.VISIBLE : View.GONE; setVisibility(visibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java new file mode 100644 index 000000000000..710e1df61404 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLogger.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +public interface IconLogger { + + void onIconShown(String tag); + void onIconHidden(String tag); + + default void onIconVisibility(String tag, boolean visible) { + if (visible) { + onIconShown(tag); + } else { + onIconHidden(tag); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java new file mode 100644 index 000000000000..0c201c3d8660 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IconLoggerImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NUM_STATUS_ICONS; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.STATUS_BAR_ICONS_CHANGED; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION; + +import android.content.Context; +import android.metrics.LogMaker; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.VisibleForTesting; +import android.util.ArraySet; + +import com.android.internal.logging.MetricsLogger; + +import java.util.Arrays; +import java.util.List; + +public class IconLoggerImpl implements IconLogger { + + // Minimum ms between log statements. + // NonFinalForTesting + @VisibleForTesting + protected static long MIN_LOG_INTERVAL = 1000; + + private final Context mContext; + private final Handler mHandler; + private final MetricsLogger mLogger; + private final ArraySet<String> mIcons = new ArraySet<>(); + private final List<String> mIconIndex; + private long mLastLog = System.currentTimeMillis(); + + public IconLoggerImpl(Context context, Looper bgLooper, MetricsLogger logger) { + mContext = context; + mHandler = new Handler(bgLooper); + mLogger = logger; + String[] icons = mContext.getResources().getStringArray( + com.android.internal.R.array.config_statusBarIcons); + mIconIndex = Arrays.asList(icons); + doLog(); + } + + @Override + public void onIconShown(String tag) { + synchronized (mIcons) { + if (mIcons.contains(tag)) return; + mIcons.add(tag); + } + if (!mHandler.hasCallbacks(mLog)) { + mHandler.postDelayed(mLog, MIN_LOG_INTERVAL); + } + } + + @Override + public void onIconHidden(String tag) { + synchronized (mIcons) { + if (!mIcons.contains(tag)) return; + mIcons.remove(tag); + } + if (!mHandler.hasCallbacks(mLog)) { + mHandler.postDelayed(mLog, MIN_LOG_INTERVAL); + } + } + + private void doLog() { + long time = System.currentTimeMillis(); + long timeSinceLastLog = time - mLastLog; + mLastLog = time; + + ArraySet<String> icons; + synchronized (mIcons) { + icons = new ArraySet<>(mIcons); + } + mLogger.write(new LogMaker(STATUS_BAR_ICONS_CHANGED) + .setType(TYPE_ACTION) + .setLatency(timeSinceLastLog) + .addTaggedData(FIELD_NUM_STATUS_ICONS, icons.size()) + .addTaggedData(FIELD_STATUS_ICONS, getBitField(icons))); + } + + private int getBitField(ArraySet<String> icons) { + int iconsVisible = 0; + for (String icon : icons) { + int index = mIconIndex.indexOf(icon); + if (index >= 0) { + iconsVisible |= (1 << index); + } + } + return iconsVisible; + } + + private final Runnable mLog = this::doLog; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 34a03bf36c47..652f8bb646d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -182,6 +182,7 @@ public class MobileSignalController extends SignalController< mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G); mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G); mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G); + mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G); if (!mConfig.showAtLeast3G) { mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 4e592dbed322..75532d9e09be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -23,14 +23,12 @@ import android.animation.PropertyValuesHolder; import android.animation.TimeAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.ColorInt; import android.annotation.FloatRange; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.PorterDuff; @@ -1083,6 +1081,20 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public ExpandableView getChildAtPosition(float touchX, float touchY) { + return getChildAtPosition(touchX, touchY, true /* requireMinHeight */); + + } + + /** + * Get the child at a certain screen location. + * + * @param touchX the x coordinate + * @param touchY the y coordinate + * @param requireMinHeight Whether a minimum height is required for a child to be returned. + * @return the child at the given location. + */ + private ExpandableView getChildAtPosition(float touchX, float touchY, + boolean requireMinHeight) { // find the view under the pointer, accounting for GONE views final int count = getChildCount(); for (int childIdx = 0; childIdx < count; childIdx++) { @@ -1101,7 +1113,7 @@ public class NotificationStackScrollLayout extends ViewGroup int left = 0; int right = getWidth(); - if (bottom - top >= mMinInteractionHeight + if ((bottom - top >= mMinInteractionHeight || !requireMinHeight) && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) { if (slidingChild instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild; @@ -3220,7 +3232,7 @@ public class NotificationStackScrollLayout extends ViewGroup case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY(); mScrolledToTopOnFirstDown = isScrolledToTop(); - if (getChildAtPosition(ev.getX(), y) == null) { + if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) { setIsBeingDragged(false); recycleVelocityTracker(); break; diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 66d00dd6c5c6..65d969932ef5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -54,6 +54,7 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { System.setProperty("dexmaker.share_classloader", "true"); + mContext.setTheme(R.style.Theme_SystemUI); SystemUIFactory.createFromConfig(mContext); mRealInstrumentation = InstrumentationRegistry.getInstrumentation(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 0500a546856d..004ff29f7eac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -21,13 +21,19 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.clearInvocations; 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 static java.lang.Thread.sleep; + import android.content.Intent; import android.metrics.LogMaker; import android.support.test.filters.SmallTest; @@ -66,10 +72,10 @@ public class QSTileImplTest extends SysuiTestCase { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mHost = mock(QSTileHost.class); when(mHost.indexOf(spec)).thenReturn(POSITION); - mTestableLooper.runWithLooper(() -> { - mTile = new TileImpl(mHost); - mTile.setTileSpec(spec); - }); + + mTile = spy(new TileImpl(mHost)); + mTile.mHandler = mTile.new H(mTestableLooper.getLooper()); + mTile.setTileSpec(spec); } @Test @@ -94,12 +100,38 @@ public class QSTileImplTest extends SysuiTestCase { public void testPopulate() { LogMaker maker = mock(LogMaker.class); when(maker.setSubtype(anyInt())).thenReturn(maker); + when(maker.addTaggedData(anyInt(), any())).thenReturn(maker); mTile.getState().value = true; mTile.populate(maker); verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1)); verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION)); } + @Test + public void testStaleTimeout() throws InterruptedException { + when(mTile.getStaleTimeout()).thenReturn(5l); + clearInvocations(mTile); + + mTile.handleRefreshState(null); + mTestableLooper.processAllMessages(); + verify(mTile, never()).handleStale(); + + sleep(10); + mTestableLooper.processAllMessages(); + verify(mTile).handleStale(); + } + + @Test + public void testStaleListening() { + mTile.handleStale(); + mTestableLooper.processAllMessages(); + verify(mTile).handleSetListening(eq(true)); + + mTile.handleRefreshState(null); + mTestableLooper.processAllMessages(); + verify(mTile).handleSetListening(eq(false)); + } + private class TileLogMatcher implements ArgumentMatcher<LogMaker> { private final int mCategory; @@ -164,7 +196,7 @@ public class QSTileImplTest extends SysuiTestCase { } @Override - protected void setListening(boolean listening) { + protected void handleSetListening(boolean listening) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index b6827eaa13bb..88fa659cd0e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -121,7 +121,7 @@ public class NotificationInfoTest extends SysuiTestCase { mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); - mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java index 54a96cd76715..83a2883c3099 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java @@ -184,7 +184,9 @@ public class NotificationInflaterTest extends SysuiTestCase { countDownLatch.await(); } + /* Cancelling requires us to be on the UI thread otherwise we might have a race */ @Test + @UiThreadTest public void testSupersedesExistingTask() throws Exception { mNotificationInflater.inflateNotificationViews(); mNotificationInflater.setIsLowPriority(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java new file mode 100644 index 000000000000..40512205a8f8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.phone; + +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.test.UiThreadTest; +import android.view.ContextThemeWrapper; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.keyguard.DismissCallbackRegistry; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class KeyguardBouncerTest extends SysuiTestCase { + + @UiThreadTest + @Test + public void inflateDetached() { + final ViewGroup container = new FrameLayout(getContext()); + final KeyguardBouncer bouncer = new KeyguardBouncer(getContext(), + mock(ViewMediatorCallback.class), mock(LockPatternUtils.class), container, mock( + DismissCallbackRegistry.class)); + + // Detached bouncer should still be able to be inflated + bouncer.inflateView(); + } + +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java index ed1491d31294..500d62012803 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java @@ -71,6 +71,24 @@ public class NearestTouchFrameTest extends SysuiTestCase { } @Test + public void testInvisibleViews() { + View left = mockViewAt(0, 0, 10, 10); + View right = mockViewAt(20, 0, 10, 10); + when(left.getVisibility()).thenReturn(View.INVISIBLE); + + mNearestTouchFrame.addView(left); + mNearestTouchFrame.addView(right); + mNearestTouchFrame.onMeasure(0, 0); + + MotionEvent ev = MotionEvent.obtain(0, 0, 0, + 12 /* x */, 5 /* y */, 0); + mNearestTouchFrame.onTouchEvent(ev); + verify(left, never()).onTouchEvent(eq(ev)); + verify(right, never()).onTouchEvent(eq(ev)); + ev.recycle(); + } + + @Test public void testHorizontalSelection_Left() { View left = mockViewAt(0, 0, 10, 10); View right = mockViewAt(20, 0, 10, 10); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java index a915cb20534a..b22a6468f5fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java @@ -27,7 +27,6 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; @@ -145,7 +144,6 @@ public class ExtensionControllerImplTest extends SysuiTestCase { @Test @RunWithLooper public void testSortOrder() { - Log.d("TestTest", "Config " + mContext.getResources().getConfiguration().uiMode); Object def = new Object(); Object uiMode = new Object(); Object tuner = new Object(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java new file mode 100644 index 000000000000..ec994a1a5650 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/IconLoggerImplTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_NUM_STATUS_ICONS; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_ICONS; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent + .NOTIFICATION_SINCE_CREATE_MILLIS; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static java.lang.Thread.sleep; + +import android.metrics.LogMaker; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.MessageHandler; +import android.testing.TestableLooper.RunWithLooper; +import android.util.Log; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class IconLoggerImplTest extends SysuiTestCase { + + private MetricsLogger mMetricsLogger; + private IconLoggerImpl mIconLogger; + private TestableLooper mTestableLooper; + private MessageHandler mMessageHandler; + + @Before + public void setup() { + IconLoggerImpl.MIN_LOG_INTERVAL = 5; // Low interval for testing + mMetricsLogger = mock(MetricsLogger.class); + mTestableLooper = TestableLooper.get(this); + mMessageHandler = mock(MessageHandler.class); + mTestableLooper.setMessageHandler(mMessageHandler); + String[] iconArray = new String[] { + "test_icon_1", + "test_icon_2", + }; + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.array.config_statusBarIcons, iconArray); + mIconLogger = new IconLoggerImpl(mContext, mTestableLooper.getLooper(), mMetricsLogger); + when(mMessageHandler.onMessageHandled(any())).thenReturn(true); + clearInvocations(mMetricsLogger); + } + + @Test + public void testIconShown() throws InterruptedException { + // Should only get one message, for the same icon shown twice. + mIconLogger.onIconShown("test_icon_2"); + mIconLogger.onIconShown("test_icon_2"); + + // There should be some delay before execute. + mTestableLooper.processAllMessages(); + verify(mMessageHandler, never()).onMessageHandled(any()); + + sleep(10); + mTestableLooper.processAllMessages(); + verify(mMessageHandler, times(1)).onMessageHandled(any()); + } + + @Test + public void testIconHidden() throws InterruptedException { + // Add the icon so that it can be removed. + mIconLogger.onIconShown("test_icon_2"); + sleep(10); + mTestableLooper.processAllMessages(); + clearInvocations(mMessageHandler); + + // Should only get one message, for the same icon shown twice. + mIconLogger.onIconHidden("test_icon_2"); + mIconLogger.onIconHidden("test_icon_2"); + + // There should be some delay before execute. + mTestableLooper.processAllMessages(); + verify(mMessageHandler, never()).onMessageHandled(any()); + + sleep(10); + mTestableLooper.processAllMessages(); + verify(mMessageHandler, times(1)).onMessageHandled(any()); + } + + @Test + public void testLog() throws InterruptedException { + mIconLogger.onIconShown("test_icon_2"); + sleep(10); + mTestableLooper.processAllMessages(); + + verify(mMetricsLogger).write(argThat(maker -> { + if (IconLoggerImpl.MIN_LOG_INTERVAL > + (long) maker.getTaggedData(NOTIFICATION_SINCE_CREATE_MILLIS)) { + Log.e("IconLoggerImplTest", "Invalid latency " + + maker.getTaggedData(NOTIFICATION_SINCE_CREATE_MILLIS)); + return false; + } + if (1 != (int) maker.getTaggedData(FIELD_NUM_STATUS_ICONS)) { + Log.e("IconLoggerImplTest", "Invalid icon count " + + maker.getTaggedData(FIELD_NUM_STATUS_ICONS)); + return false; + } + return true; + })); + } + + @Test + public void testBitField() throws InterruptedException { + mIconLogger.onIconShown("test_icon_2"); + sleep(10); + mTestableLooper.processAllMessages(); + + verify(mMetricsLogger).write(argThat(maker -> { + if ((1 << 1) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) { + Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString( + (Integer) maker.getTaggedData(FIELD_NUM_STATUS_ICONS))); + return false; + } + return true; + })); + + mIconLogger.onIconShown("test_icon_1"); + sleep(10); + mTestableLooper.processAllMessages(); + + verify(mMetricsLogger).write(argThat(maker -> { + if ((1 << 1 | 1 << 0) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) { + Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString( + (Integer) maker.getTaggedData(FIELD_NUM_STATUS_ICONS))); + return false; + } + return true; + })); + + mIconLogger.onIconHidden("test_icon_2"); + sleep(10); + mTestableLooper.processAllMessages(); + + verify(mMetricsLogger).write(argThat(maker -> { + if ((1 << 0) != (int) maker.getTaggedData(FIELD_STATUS_ICONS)) { + Log.e("IconLoggerImplTest", "Invalid bitfield " + Integer.toHexString( + (Integer) maker.getTaggedData(FIELD_STATUS_ICONS))); + return false; + } + return true; + })); + } +} diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml b/packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml new file mode 100644 index 000000000000..c56382e6047f --- /dev/null +++ b/packages/overlays/SysuiDarkThemeOverlay/res/color/qs_detail_progress_track.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- I really don't want to define this, but the View that uses this asset uses both the + light and dark accent colors. --> + <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_light" /> +</selector> |