diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4ba757fbdfed8..2427d36076c58 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2829,4 +2829,9 @@ Couldn\'t connect. Try again. Pair new device + + + Build number + + Build number copied to clipboard. diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index d0e55a4652f4c..84563a0784472 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -20,6 +20,8 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -34,6 +36,7 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; @@ -154,6 +157,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mActionsContainer = findViewById(R.id.qs_footer_actions_container); mEditContainer = findViewById(R.id.qs_footer_actions_edit_container); mBuildText = findViewById(R.id.build); + mBuildText.setOnLongClickListener(view -> { + CharSequence buildText = mBuildText.getText(); + if (!TextUtils.isEmpty(buildText)) { + ClipboardManager service = + mUserTracker.getUserContext().getSystemService(ClipboardManager.class); + String label = mContext.getString(R.string.build_number_clip_data_label); + service.setPrimaryClip(ClipData.newPlainText(label, buildText)); + Toast.makeText(mContext, R.string.build_number_copy_toast, Toast.LENGTH_SHORT) + .show(); + return true; + } + return false; + }); // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view @@ -180,6 +196,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mBuildText.setSelected(true); mShouldShowBuildText = true; } else { + mBuildText.setText(null); mShouldShowBuildText = false; mBuildText.setSelected(false); } @@ -321,6 +338,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE); mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE); mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE); + mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE); } private void updateVisibilities() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java index 207c911148c3a..99f2d80425470 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java @@ -14,17 +14,22 @@ package com.android.systemui.qs; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ClipData; +import android.content.ClipboardManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import android.view.View; +import android.widget.TextView; import androidx.test.filters.SmallTest; @@ -33,12 +38,16 @@ import com.android.systemui.R.id; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.utils.leaks.LeakCheckedTest; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -48,19 +57,45 @@ public class QSFooterImplTest extends LeakCheckedTest { private QSFooterImpl mFooter; private ActivityStarter mActivityStarter; private DeviceProvisionedController mDeviceProvisionedController; + private UserInfoController mUserInfoController; + private UserTracker mUserTracker; + @Mock + private ClipboardManager mClipboardManager; @Before public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class); mDeviceProvisionedController = mDependency.injectMockDependency( DeviceProvisionedController.class); - mDependency.injectMockDependency(UserTracker.class); + mUserInfoController = mDependency.injectMockDependency(UserInfoController.class); + mUserTracker = mDependency.injectMockDependency(UserTracker.class); + + mContext.addMockSystemService(ClipboardManager.class, mClipboardManager); + + when(mUserTracker.getUserContext()).thenReturn(mContext); + TestableLooper.get(this).runWithLooper( () -> mFooter = (QSFooterImpl) LayoutInflater.from(mContext).inflate( R.layout.qs_footer_impl, null)); } + @Test + public void testBuildTextCopy() { + TextView buildTextView = mFooter.requireViewById(R.id.build); + CharSequence buildText = "TEST"; + buildTextView.setText(buildText); + buildTextView.setLongClickable(true); + + buildTextView.performLongClick(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ClipData.class); + verify(mClipboardManager).setPrimaryClip(captor.capture()); + assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(buildText); + } + @Test @Ignore("failing") public void testSettings_UserNotSetup() {