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() {