From 437cdb146498cbfabd6ed0c2466fc5349b0f1d65 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 3 Jan 2018 12:27:24 -0500 Subject: [PATCH] Inline noti blocking is now in the form of a question And has an 'undo' flow that matches snoozing. Test: runtest systemui Bug: 63927402 Change-Id: Idc17b8d950bc3da7ec9fe035dc8a65146d12c456 --- core/res/res/values/dimens.xml | 4 +- packages/SystemUI/res/drawable/ic_info.xml | 25 + .../SystemUI/res/layout/notification_info.xml | 162 +++-- packages/SystemUI/res/values/dimens.xml | 6 + packages/SystemUI/res/values/strings.xml | 47 +- .../statusbar/NotificationGutsManager.java | 7 +- .../systemui/statusbar/NotificationInfo.java | 363 +++++----- .../statusbar/NotificationInfoTest.java | 639 ++++-------------- .../src/android/testing/PollingCheck.java | 94 +++ 9 files changed, 547 insertions(+), 800 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_info.xml create mode 100644 tests/testables/src/android/testing/PollingCheck.java diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a659b370560f3..14d4c02a8d392 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -193,10 +193,10 @@ 49.5dp - 14dp + 16dp - 13dp + 16dp 5dp diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/SystemUI/res/drawable/ic_info.xml new file mode 100644 index 0000000000000..1eb4004ac7789 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_info.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 2e35d8673635e..6b5bd1260acc1 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -23,23 +23,22 @@ android:clickable="true" android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" + android:paddingEnd="@*android:dimen/notification_content_margin_end" android:background="@color/notification_guts_bg_color" android:theme="@*android:style/Theme.DeviceDefault.Light"> - + android:clipToPadding="false" + android:layout_marginTop="@*android:dimen/notification_header_padding_top" > + android:layout_width="@dimen/notification_guts_header_height" + android:layout_height="@dimen/notification_guts_header_height" + android:layout_marginEnd="3dp" /> + android:singleLine="true" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkgicon" /> + android:text="@*android:string/notification_header_divider_symbol" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkgname" /> - + android:maxLines="1" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkg_group_divider" /> + + - - + + + android:orientation="vertical"> - - + + style="@style/TextAppearance.NotificationInfo.Secondary" /> - - - - - - - - - - + + + + + + + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 60e9ebf4ba5d8..8f2068111cd80 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -148,6 +148,12 @@ 20dp + + 20dp + + + 24dp + 56dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 98537a102e1a0..a19917d916673 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1445,38 +1445,22 @@ Notifications - You won\'t get these notifications anymore + You won\'t see these notifications anymore - - %d notification categories + + Keep showing these notifications? - - This app doesn\'t have notification categories + + Stop notifications + + + Keep showing + + + Keep showing notifications from this app? - Notifications from this app can\'t be turned off - - - - 1 out of %s notification category from this app - 1 out of %s notification categories from this app - - - - %1$s, %2$s - - %1$s, %2$s, and %3$d other - %1$s, %2$s, and %3$d others - + These notifications can\'t be turned off @@ -1487,17 +1471,16 @@ Allow notifications from this channel - - All Categories More settings - Customize: %1$s + Customize Done + + Undo %1$s %2$s diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 87ad6f6bc9484..441c184318949 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -229,10 +229,9 @@ public class NotificationGutsManager implements Dumpable { } } try { - info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels), - row.getEntry().channel.getImportance(), sbn, onSettingsClick, - onAppSettingsClick, onDoneClick, mCheckSaveListener, - mNonBlockablePkgs); + info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel, + channels.size(), sbn, mCheckSaveListener, onSettingsClick, + onAppSettingsClick, mNonBlockablePkgs); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 8d1bb5fe940d1..6279fdc465999 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -18,6 +18,10 @@ package com.android.systemui.statusbar; import static android.app.NotificationManager.IMPORTANCE_NONE; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; @@ -38,12 +42,12 @@ import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.Switch; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; +import com.android.systemui.Interpolators; import com.android.systemui.R; import java.lang.IllegalArgumentException; @@ -57,25 +61,37 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private static final String TAG = "InfoGuts"; private INotificationManager mINotificationManager; + private PackageManager mPm; + private String mPkg; private String mAppName; private int mAppUid; - private List mNotificationChannels; + private int mNumNotificationChannels; private NotificationChannel mSingleNotificationChannel; - private boolean mIsSingleDefaultChannel; - private StatusBarNotification mSbn; private int mStartingUserImportance; + private int mChosenImportance; + private boolean mIsSingleDefaultChannel; + private boolean mNonblockable; + private StatusBarNotification mSbn; + private AnimatorSet mExpandAnimation; - private TextView mNumChannelsView; - private View mChannelDisabledView; - private TextView mSettingsLinkView; - private Switch mChannelEnabledSwitch; private CheckSaveListener mCheckSaveListener; + private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; - private PackageManager mPm; - private NotificationGuts mGutsContainer; + private OnClickListener mOnKeepShowing = v -> { + closeControls(v); + }; + + private OnClickListener mOnStopNotifications = v -> { + swapContent(false); + }; + + private OnClickListener mOnUndo = v -> { + swapContent(true); + }; + public NotificationInfo(Context context, AttributeSet attrs) { super(context, attrs); } @@ -98,141 +114,93 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, final String pkg, - final List notificationChannels, - int startingUserImportance, + final NotificationChannel notificationChannel, + final int numChannels, final StatusBarNotification sbn, - OnSettingsClickListener onSettingsClick, - OnAppSettingsClickListener onAppSettingsClick, - OnClickListener onDoneClick, - CheckSaveListener checkSaveListener, + final CheckSaveListener checkSaveListener, + final OnSettingsClickListener onSettingsClick, + final OnAppSettingsClickListener onAppSettingsClick, final Set nonBlockablePkgs) throws RemoteException { mINotificationManager = iNotificationManager; mPkg = pkg; - mNotificationChannels = notificationChannels; - mCheckSaveListener = checkSaveListener; + mNumNotificationChannels = numChannels; mSbn = sbn; mPm = pm; mAppSettingsClickListener = onAppSettingsClick; - mStartingUserImportance = startingUserImportance; mAppName = mPkg; - Drawable pkgicon = null; - CharSequence channelNameText = ""; - ApplicationInfo info = null; - try { - info = pm.getApplicationInfo(mPkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (info != null) { - mAppUid = sbn.getUid(); - mAppName = String.valueOf(pm.getApplicationLabel(info)); - pkgicon = pm.getApplicationIcon(info); - } - } catch (PackageManager.NameNotFoundException e) { - // app is gone, just show package name and generic icon - pkgicon = pm.getDefaultActivityIcon(); - } - ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); + mCheckSaveListener = checkSaveListener; + mOnSettingsClickListener = onSettingsClick; + mSingleNotificationChannel = notificationChannel; + mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); - int numTotalChannels = iNotificationManager.getNumNotificationChannelsForPackage( + int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); - if (mNotificationChannels.isEmpty()) { + if (mNumNotificationChannels == 0) { throw new IllegalArgumentException("bindNotification requires at least one channel"); } else { - if (mNotificationChannels.size() == 1) { - mSingleNotificationChannel = mNotificationChannels.get(0); - // Special behavior for the Default channel if no other channels have been defined. - mIsSingleDefaultChannel = - (mSingleNotificationChannel.getId() - .equals(NotificationChannel.DEFAULT_CHANNEL_ID) && - numTotalChannels <= 1); - } else { - mSingleNotificationChannel = null; - mIsSingleDefaultChannel = false; - } + // Special behavior for the Default channel if no other channels have been defined. + mIsSingleDefaultChannel = mNumNotificationChannels == 1 + && mSingleNotificationChannel.getId() + .equals(NotificationChannel.DEFAULT_CHANNEL_ID) + && numTotalChannels <= 1; } - boolean nonBlockable = false; try { final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) { - final int numChannels = mNotificationChannels.size(); - for (int i = 0; i < numChannels; i++) { - final NotificationChannel notificationChannel = mNotificationChannels.get(i); - // If any of the system channels is not blockable, the bundle is nonblockable - if (!notificationChannel.isBlockableSystem()) { - nonBlockable = true; - break; - } + if (mSingleNotificationChannel != null + && !mSingleNotificationChannel.isBlockableSystem()) { + mNonblockable = true; } } } catch (PackageManager.NameNotFoundException e) { // unlikely. } if (nonBlockablePkgs != null) { - nonBlockable |= nonBlockablePkgs.contains(pkg); + mNonblockable |= nonBlockablePkgs.contains(pkg); } + mNonblockable |= (mNumNotificationChannels > 1); - String channelsDescText; - mNumChannelsView = findViewById(R.id.num_channels_desc); - if (nonBlockable) { - channelsDescText = mContext.getString(R.string.notification_unblockable_desc); - } else if (mIsSingleDefaultChannel) { - channelsDescText = mContext.getString(R.string.notification_default_channel_desc); - } else { - switch (mNotificationChannels.size()) { - case 1: - channelsDescText = String.format(mContext.getResources().getQuantityString( - R.plurals.notification_num_channels_desc, numTotalChannels), - numTotalChannels); - break; - case 2: - channelsDescText = mContext.getString( - R.string.notification_channels_list_desc_2, - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName()); - break; - default: - final int numOthers = mNotificationChannels.size() - 2; - channelsDescText = String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_channels_list_desc_2_and_others, - numOthers), - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName(), - numOthers); + bindHeader(); + bindPrompt(); + bindButtons(); + } + + private void bindHeader() throws RemoteException { + // Package name + Drawable pkgicon = null; + ApplicationInfo info; + try { + info = mPm.getApplicationInfo(mPkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppUid = mSbn.getUid(); + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + pkgicon = mPm.getApplicationIcon(info); } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + pkgicon = mPm.getDefaultActivityIcon(); } - mNumChannelsView.setText(channelsDescText); - - if (mSingleNotificationChannel == null) { - // Multiple channels don't use a channel name for the title. - channelNameText = mContext.getString(R.string.notification_num_channels, - mNotificationChannels.size()); - } else if (mIsSingleDefaultChannel || nonBlockable) { - // If this is the default channel or the app is unblockable, - // don't use our channel-specific text. - channelNameText = mContext.getString(R.string.notification_header_default_channel); - } else { - channelNameText = mSingleNotificationChannel.getName(); - } + ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); ((TextView) findViewById(R.id.pkgname)).setText(mAppName); - ((TextView) findViewById(R.id.channel_name)).setText(channelNameText); // Set group information if this channel has an associated group. CharSequence groupName = null; if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { final NotificationChannelGroup notificationChannelGroup = - iNotificationManager.getNotificationChannelGroupForPackage( - mSingleNotificationChannel.getGroup(), pkg, mAppUid); + mINotificationManager.getNotificationChannelGroupForPackage( + mSingleNotificationChannel.getGroup(), mPkg, mAppUid); if (notificationChannelGroup != null) { groupName = notificationChannelGroup.getName(); } } - TextView groupNameView = ((TextView) findViewById(R.id.group_name)); - TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider)); + TextView groupNameView = findViewById(R.id.group_name); + TextView groupDividerView = findViewById(R.id.pkg_group_divider); if (groupName != null) { groupNameView.setText(groupName); groupNameView.setVisibility(View.VISIBLE); @@ -242,53 +210,55 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G groupDividerView.setVisibility(View.GONE); } - bindButtons(nonBlockable); - - // Top-level importance group - mChannelDisabledView = findViewById(R.id.channel_disabled); - updateSecondaryText(); - // Settings button. - final TextView settingsButton = (TextView) findViewById(R.id.more_settings); - if (mAppUid >= 0 && onSettingsClick != null) { + final View settingsButton = findViewById(R.id.info); + if (mAppUid >= 0 && mOnSettingsClickListener != null) { settingsButton.setVisibility(View.VISIBLE); final int appUidF = mAppUid; settingsButton.setOnClickListener( (View view) -> { - onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF); + mOnSettingsClickListener.onClick(view, + mNumNotificationChannels > 1 ? null : mSingleNotificationChannel, + appUidF); }); - if (numTotalChannels <= 1 || nonBlockable) { - settingsButton.setText(R.string.notification_more_settings); - } else { - settingsButton.setText(R.string.notification_all_categories); - } } else { settingsButton.setVisibility(View.GONE); } + } - // Done button. - final TextView doneButton = (TextView) findViewById(R.id.done); - doneButton.setText(R.string.notification_done); - doneButton.setOnClickListener(onDoneClick); + private void bindPrompt() { + final TextView channelName = findViewById(R.id.channel_name); + final TextView blockPrompt = findViewById(R.id.block_prompt); + if (mNonblockable) { + if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + channelName.setVisibility(View.GONE); + } else { + channelName.setText(mSingleNotificationChannel.getName()); + } - // Optional settings link - updateAppSettingsLink(); + blockPrompt.setText(R.string.notification_unblockable_desc); + } else { + if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + channelName.setVisibility(View.GONE); + blockPrompt.setText(R.string.inline_keep_showing_app); + } else { + channelName.setText(mSingleNotificationChannel.getName()); + blockPrompt.setText(R.string.inline_keep_showing); + } + } } private boolean hasImportanceChanged() { - return mSingleNotificationChannel != null && - mChannelEnabledSwitch != null && - mStartingUserImportance != getSelectedImportance(); + return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance; } private void saveImportance() { - if (!hasImportanceChanged()) { + if (mNonblockable || !hasImportanceChanged()) { return; } - final int selectedImportance = getSelectedImportance(); MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - selectedImportance - mStartingUserImportance); - mSingleNotificationChannel.setImportance(selectedImportance); + mChosenImportance - mStartingUserImportance); + mSingleNotificationChannel.setImportance(mChosenImportance); mSingleNotificationChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); try { mINotificationManager.updateNotificationChannelForPackage( @@ -298,30 +268,78 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private int getSelectedImportance() { - if (!mChannelEnabledSwitch.isChecked()) { - return IMPORTANCE_NONE; + private void bindButtons() { + View block = findViewById(R.id.block); + block.setOnClickListener(mOnStopNotifications); + TextView keep = findViewById(R.id.keep); + keep.setOnClickListener(mOnKeepShowing); + findViewById(R.id.undo).setOnClickListener(mOnUndo); + + if (mNonblockable) { + keep.setText(R.string.notification_done); + block.setVisibility(GONE); + } + + // app settings link + TextView settingsLinkView = findViewById(R.id.app_settings); + Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, + mSbn.getId(), mSbn.getTag()); + if (settingsIntent != null + && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { + settingsLinkView.setVisibility(View.VISIBLE); + settingsLinkView.setText(mContext.getString(R.string.notification_app_settings, + mSbn.getNotification().getSettingsText())); + settingsLinkView.setOnClickListener((View view) -> { + mAppSettingsClickListener.onClick(view, settingsIntent); + }); } else { - return mStartingUserImportance; + settingsLinkView.setVisibility(View.GONE); } } - private void bindButtons(final boolean nonBlockable) { - // Enabled Switch - mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch); - mChannelEnabledSwitch.setChecked( - mStartingUserImportance != IMPORTANCE_NONE); - final boolean visible = !nonBlockable && mSingleNotificationChannel != null; - mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + private void swapContent(boolean showPrompt) { + if (mExpandAnimation != null) { + mExpandAnimation.cancel(); + } - // Callback when checked. - mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (mGutsContainer != null) { - mGutsContainer.resetFalsingCheck(); + if (showPrompt) { + mChosenImportance = mStartingUserImportance; + } else { + mChosenImportance = IMPORTANCE_NONE; + } + + View prompt = findViewById(R.id.prompt); + View confirmation = findViewById(R.id.confirmation); + ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA, + prompt.getAlpha(), showPrompt ? 1f : 0f); + promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); + ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA, + confirmation.getAlpha(), showPrompt ? 0f : 1f); + confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); + + prompt.setVisibility(showPrompt ? VISIBLE : GONE); + confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + + mExpandAnimation = new AnimatorSet(); + mExpandAnimation.playTogether(promptAnim, confirmAnim); + mExpandAnimation.setDuration(150); + mExpandAnimation.addListener(new AnimatorListenerAdapter() { + boolean cancelled = false; + + @Override + public void onAnimationCancel(Animator animation) { + cancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!cancelled) { + prompt.setVisibility(showPrompt ? VISIBLE : GONE); + confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + } } - updateSecondaryText(); - updateAppSettingsLink(); }); + mExpandAnimation.start(); } @Override @@ -339,35 +357,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void updateSecondaryText() { - final boolean disabled = mSingleNotificationChannel != null && - getSelectedImportance() == IMPORTANCE_NONE; - if (disabled) { - mChannelDisabledView.setVisibility(View.VISIBLE); - mNumChannelsView.setVisibility(View.GONE); - } else { - mChannelDisabledView.setVisibility(View.GONE); - mNumChannelsView.setVisibility(mIsSingleDefaultChannel ? View.INVISIBLE : View.VISIBLE); - } - } - - private void updateAppSettingsLink() { - mSettingsLinkView = findViewById(R.id.app_settings); - Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, - mSbn.getId(), mSbn.getTag()); - if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE - && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { - mSettingsLinkView.setVisibility(View.VISIBLE); - mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings, - mSbn.getNotification().getSettingsText())); - mSettingsLinkView.setOnClickListener((View view) -> { - mAppSettingsClickListener.onClick(view, settingsIntent); - }); - } else { - mSettingsLinkView.setVisibility(View.GONE); - } - } - private Intent getAppSettingsIntent(PackageManager pm, String packageName, NotificationChannel channel, int id, String tag) { Intent intent = new Intent(Intent.ACTION_MAIN) @@ -390,6 +379,18 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return intent; } + private void closeControls(View v) { + int[] parentLoc = new int[2]; + int[] targetLoc = new int[2]; + mGutsContainer.getLocationOnScreen(parentLoc); + v.getLocationOnScreen(targetLoc); + final int centerX = v.getWidth() / 2; + final int centerY = v.getHeight() / 2; + final int x = targetLoc[0] - parentLoc[0] + centerX; + final int y = targetLoc[1] - parentLoc[1] + centerY; + mGutsContainer.closeControls(x, y, false /* save */, false /* force */); + } + @Override public void setGutsParent(NotificationGuts guts) { mGutsContainer = guts; @@ -397,7 +398,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Override public boolean willBeRemoved() { - return mChannelEnabledSwitch != null && !mChannelEnabledSwitch.isChecked(); + return hasImportanceChanged(); } @Override 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 672193835ac3b..d77bf69ad3650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -16,11 +16,13 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.any; @@ -50,11 +52,11 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.PollingCheck; import android.testing.UiThreadTest; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; -import android.widget.Switch; import android.widget.TextView; import com.android.systemui.R; @@ -66,7 +68,6 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -117,50 +118,31 @@ public class NotificationInfoTest extends SysuiTestCase { // Some test channels. mNotificationChannel = new NotificationChannel( - TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, - NotificationManager.IMPORTANCE_LOW); + IMPORTANCE_LOW); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); } - private CharSequence getStringById(int resId) { - return mContext.getString(resId); + // TODO: if tests are taking too long replace this with something that makes the animation + // finish instantly. + private void waitForUndoButton() { + PollingCheck.waitFor(1000, + () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()); } - - private CharSequence getNumChannelsDescString(int numChannels) { - return String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_num_channels_desc, numChannels), - numChannels); - } - - private CharSequence getChannelsListDescString(NotificationChannel... channels) { - if (channels.length == 2) { - return mContext.getString(R.string.notification_channels_list_desc_2, - channels[0].getName(), channels[1].getName()); - } else { - final int numOthers = channels.length - 2; - return String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_channels_list_desc_2_and_others, numOthers), - channels[0].getName(), channels[1].getName(), numOthers); - } - } - - private CharSequence getNumChannelsString(int numChannels) { - return mContext.getString(R.string.notification_num_channels, numChannels); + private void waitForStopButton() { + PollingCheck.waitFor(1000, + () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility()); } @Test public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); } @@ -170,24 +152,19 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); - assertEquals(View.GONE, groupNameView.getVisibility()); - final TextView groupDividerView = - (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider); - assertEquals(View.GONE, groupDividerView.getVisibility()); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); + assertEquals(GONE, groupNameView.getVisibility()); + final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); + assertEquals(GONE, groupDividerView.getVisibility()); } @Test @@ -199,75 +176,50 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); - final TextView groupDividerView = - (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider); + final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); assertEquals(View.VISIBLE, groupDividerView.getVisibility()); } @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @Test public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mContext.getString(R.string.notification_header_default_channel), - textView.getText()); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(GONE, textView.getVisibility()); } @Test - public void testBindNotification_UnblockablePackageDoesNotUseChannelName() throws Exception { + public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mContext.getString(R.string.notification_header_default_channel), - textView.getText()); - } - - @Test - public void testBindNotification_DefaultChannelUsesNameWhenMoreThanOneChannelExists() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mDefaultNotificationChannel.getName(), textView.getText()); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(VISIBLE, textView.getVisibility()); } @Test public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, null, null, null); + }, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); @@ -276,24 +228,20 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @Test - public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception { + public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, + (View v, NotificationChannel c, int appUid) -> { + }, null, null); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -301,279 +249,63 @@ public class NotificationInfoTest extends SysuiTestCase { public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, null, null, null); + }, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - settingsButton.performClick(); + mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); } - @Test - public void testBindNotification_SettingsTextWithOneChannel() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText()); - } - - @Test - public void testBindNotification_SettingsTextWithMultipleChannels() throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText()); - } - - @Test - public void testBindNotification_SettingsTextWithMultipleChannelsForUnblockableApp() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText()); - } - - @Test - public void testBindNotification_SetsOnClickListenerForDone() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, - null, (View v) -> { - latch.countDown(); - }, - null, null); - - final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done); - doneButton.performClick(); - // Verify that listener was triggered. - assertEquals(0, latch.getCount()); - } - - @Test - public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(View.INVISIBLE, numChannelsView.getVisibility()); - } - - @Test - public void testBindNotification_NumChannelsTextDisplaysWhenMoreThanOneChannelExists() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.VISIBLE); - assertEquals(getNumChannelsDescString(2), numChannelsView.getText()); - } - - @Test - public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel() - throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.VISIBLE); - assertEquals(getNumChannelsDescString(1), numChannelsView.getText()); - } - - @Test - public void testBindNotification_NumChannelsTextScalesWithNumberOfChannels() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(getNumChannelsDescString(2), numChannelsView.getText()); - } - @Test @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenTwoInBundle() + public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenThreeInBundle() - throws Exception { - NotificationChannel thirdChannel = new NotificationChannel( - "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals( - getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel, - thirdChannel), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenFourInBundle() - throws Exception { - NotificationChannel thirdChannel = new NotificationChannel( - "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW); - NotificationChannel fourthChannel = new NotificationChannel( - "fourth_channel", "fourth_channel", NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel, - fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals( - getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel, - thirdChannel, fourthChannel), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_ChannelNameChangesWhenBundleFromDifferentChannels() - throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); final TextView channelNameView = - (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(getNumChannelsString(2), channelNameView.getText()); + mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(GONE, channelNameView.getVisibility()); } @Test @UiThreadTest - public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception { + public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(channelDisabledView.getVisibility(), View.GONE); - } - - @Test - public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(channelDisabledView.getVisibility(), View.VISIBLE); - // Replaces the numChannelsView - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.GONE); + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + final TextView blockView = mNotificationInfo.findViewById(R.id.block); + assertEquals(GONE, blockView.getVisibility()); } @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(View.VISIBLE, numChannelsView.getVisibility()); + final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); + assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_ChannelDisabledTextShowsForDefaultChannel() - throws Exception { - mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mDefaultNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(View.VISIBLE, channelDisabledView.getVisibility()); + view.getText()); } @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @Test public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @@ -582,9 +314,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -596,124 +326,33 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } - @Test - public void testEnabledSwitchOnByDefault() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertTrue(enabledSwitch.isChecked()); - } - - @Test - public void testEnabledButtonOffWhenAlreadyBanned() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertFalse(enabledSwitch.isChecked()); - } - - @Test - public void testEnabledSwitchVisibleByDefault() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchInvisibleIfNonBlockableSystemChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(false); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchVisibleIfBlockableSystemChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(true); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchInvisibleIfMultiChannelSummary() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(true); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - @Test public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - mNotificationInfo.handleCloseControls(true, false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @Test - public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); - Switch enabledSwitch = mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); ArgumentCaptor updated = @@ -725,15 +364,35 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + public void testBlockUndoDoesNotCallUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + mNotificationInfo.findViewById(R.id.undo).performClick(); + waitForStopButton(); + mNotificationInfo.handleCloseControls(true, false); + + ArgumentCaptor updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance()); + } + + @Test + public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(false, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -741,16 +400,14 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, - Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -758,17 +415,15 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, - Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -785,22 +440,19 @@ public class NotificationInfoTest extends SysuiTestCase { List ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null, null, null); + }, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); - assertTrue(settingsLink.getText().toString().length() > settingsText.length()); - assertTrue(settingsLink.getText().toString().contains(settingsText)); settingsLink.performClick(); assertEquals(0, latch.getCount()); } @@ -816,17 +468,17 @@ public class NotificationInfoTest extends SysuiTestCase { List ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> { + TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, + (View v, Intent intent) -> { latch.countDown(); - }, null, null, null); + }, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -837,18 +489,16 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNoSettingsLink_noHandlingActivity() throws Exception { final String settingsText = "work chats"; when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.GONE, settingsLink.getVisibility()); + assertEquals(GONE, settingsLink.getVisibility()); } @Test @@ -860,52 +510,17 @@ public class NotificationInfoTest extends SysuiTestCase { List ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.GONE, settingsLink.getVisibility()); + assertEquals(GONE, settingsLink.getVisibility()); } - @Test - public void testNoSettingsLink_afterBlockingChannel() throws Exception { - final String settingsText = "work chats"; - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - - // Block channel - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); - - assertEquals(View.GONE, settingsLink.getVisibility()); - - //unblock - enabledSwitch.setChecked(true); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - } @Test public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception { diff --git a/tests/testables/src/android/testing/PollingCheck.java b/tests/testables/src/android/testing/PollingCheck.java new file mode 100644 index 0000000000000..5a31450f048f8 --- /dev/null +++ b/tests/testables/src/android/testing/PollingCheck.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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 android.testing; + +import junit.framework.Assert; +import java.util.concurrent.Callable; + +public abstract class PollingCheck { + private static final long TIME_SLICE = 50; + private long mTimeout = 3000; + + public static interface PollingCheckCondition { + boolean canProceed(); + } + + public PollingCheck() { + } + + public PollingCheck(long timeout) { + mTimeout = timeout; + } + + protected abstract boolean check(); + + public void run() { + if (check()) { + return; + } + + long timeout = mTimeout; + while (timeout > 0) { + try { + Thread.sleep(TIME_SLICE); + } catch (InterruptedException e) { + Assert.fail("unexpected InterruptedException"); + } + + if (check()) { + return; + } + + timeout -= TIME_SLICE; + } + + Assert.fail("unexpected timeout"); + } + + public static void check(CharSequence message, long timeout, Callable condition) + throws Exception { + while (timeout > 0) { + if (condition.call()) { + return; + } + + Thread.sleep(TIME_SLICE); + timeout -= TIME_SLICE; + } + + Assert.fail(message.toString()); + } + + public static void waitFor(final PollingCheckCondition condition) { + new PollingCheck() { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } + + public static void waitFor(long timeout, final PollingCheckCondition condition) { + new PollingCheck(timeout) { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } +} +