Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +7 −1 Original line number Diff line number Diff line Loading @@ -93,7 +93,13 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "User has discarded the result of a long screenshot") SCREENSHOT_LONG_SCREENSHOT_EXIT(911), @UiEvent(doc = "A screenshot has been taken and saved to work profile") SCREENSHOT_SAVED_TO_WORK_PROFILE(1240); SCREENSHOT_SAVED_TO_WORK_PROFILE(1240), @UiEvent(doc = "Notes application triggered the screenshot for notes") SCREENSHOT_FOR_NOTE_TRIGGERED(1308), @UiEvent(doc = "User accepted the screenshot to be sent to the notes app") SCREENSHOT_FOR_NOTE_ACCEPTED(1309), @UiEvent(doc = "User cancelled the screenshot for notes app flow") SCREENSHOT_FOR_NOTE_CANCELLED(1310); private final int mId; Loading packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java +46 −2 Original line number Diff line number Diff line Loading @@ -17,15 +17,21 @@ package com.android.systemui.screenshot; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; Loading @@ -33,15 +39,20 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import androidx.activity.ComponentActivity; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger.UiEventEnum; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.settings.UserTracker; import javax.inject.Inject; Loading @@ -64,9 +75,15 @@ import javax.inject.Inject; * * TODO(b/267309532): Polish UI and animations. */ public final class AppClipsActivity extends ComponentActivity { public class AppClipsActivity extends ComponentActivity { private static final String TAG = AppClipsActivity.class.getSimpleName(); private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0); private final AppClipsViewModel.Factory mViewModelFactory; private final PackageManager mPackageManager; private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; private final BroadcastReceiver mBroadcastReceiver; private final IntentFilter mIntentFilter; Loading @@ -80,10 +97,17 @@ public final class AppClipsActivity extends ComponentActivity { private AppClipsViewModel mViewModel; private ResultReceiver mResultReceiver; @Nullable private String mCallingPackageName; private int mCallingPackageUid; @Inject public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory) { public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory, PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger) { mViewModelFactory = viewModelFactory; mPackageManager = packageManager; mUserTracker = userTracker; mUiEventLogger = uiEventLogger; mBroadcastReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -113,6 +137,7 @@ public final class AppClipsActivity extends ComponentActivity { RECEIVER_NOT_EXPORTED); Intent intent = getIntent(); setUpUiLogging(intent); mResultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER, ResultReceiver.class); if (mResultReceiver == null) { setErrorThenFinish(Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED); Loading Loading @@ -169,6 +194,17 @@ public final class AppClipsActivity extends ComponentActivity { } } private void setUpUiLogging(Intent intent) { mCallingPackageName = intent.getStringExtra(EXTRA_CALLING_PACKAGE_NAME); mCallingPackageUid = 0; try { mCallingPackageUid = mPackageManager.getApplicationInfoAsUser(mCallingPackageName, APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid; } catch (NameNotFoundException e) { Log.d(TAG, "Couldn't find notes app UID " + e); } } private void setScreenshot(Bitmap screenshot) { // Set background, status and navigation bar colors as the activity is no longer // translucent. Loading Loading @@ -228,6 +264,7 @@ public final class AppClipsActivity extends ComponentActivity { data.putParcelable(EXTRA_SCREENSHOT_URI, uri); try { mResultReceiver.send(Activity.RESULT_OK, data); logUiEvent(SCREENSHOT_FOR_NOTE_ACCEPTED); } catch (Exception e) { // Do nothing. } Loading @@ -251,6 +288,9 @@ public final class AppClipsActivity extends ComponentActivity { data.putInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE, errorCode); try { mResultReceiver.send(RESULT_OK, data); if (errorCode == Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED) { logUiEvent(SCREENSHOT_FOR_NOTE_CANCELLED); } } catch (Exception e) { // Do nothing. } Loading @@ -259,6 +299,10 @@ public final class AppClipsActivity extends ComponentActivity { mResultReceiver = null; } private void logUiEvent(UiEventEnum uiEvent) { mUiEventLogger.log(uiEvent, mCallingPackageUid, mCallingPackageName); } private void updateImageDimensions() { Drawable drawable = mPreview.getDrawable(); if (drawable == null) { Loading packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java +43 −5 Original line number Diff line number Diff line Loading @@ -24,26 +24,33 @@ import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcel; import android.os.ResultReceiver; import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.settings.UserTracker; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; Loading @@ -70,15 +77,23 @@ import javax.inject.Inject; public class AppClipsTrampolineActivity extends Activity { private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName(); public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI"; public static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE"; static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER"; static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME"; private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0); private final DevicePolicyManager mDevicePolicyManager; private final FeatureFlags mFeatureFlags; private final Optional<Bubbles> mOptionalBubbles; private final NoteTaskController mNoteTaskController; private final PackageManager mPackageManager; private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; private final ResultReceiver mResultReceiver; private Intent mKillAppClipsBroadcastIntent; Loading @@ -86,11 +101,15 @@ public class AppClipsTrampolineActivity extends Activity { @Inject public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags, Optional<Bubbles> optionalBubbles, NoteTaskController noteTaskController, PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger, @Main Handler mainHandler) { mDevicePolicyManager = devicePolicyManager; mFeatureFlags = flags; mOptionalBubbles = optionalBubbles; mNoteTaskController = noteTaskController; mPackageManager = packageManager; mUserTracker = userTracker; mUiEventLogger = uiEventLogger; mResultReceiver = createResultReceiver(mainHandler); } Loading Loading @@ -138,8 +157,12 @@ public class AppClipsTrampolineActivity extends Activity { return; } Intent intent = new Intent().setComponent(componentName).addFlags( Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver); String callingPackageName = getCallingPackage(); Intent intent = new Intent().setComponent(componentName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver) .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName); try { // Start the App Clips activity. startActivity(intent); Loading @@ -150,6 +173,9 @@ public class AppClipsTrampolineActivity extends Activity { new Intent(ACTION_FINISH_FROM_TRAMPOLINE) .setComponent(componentName) .setPackage(componentName.getPackageName()); // Log successful triggering of screenshot for notes. logScreenshotTriggeredUiEvent(callingPackageName); } catch (ActivityNotFoundException e) { setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); } Loading @@ -170,6 +196,18 @@ public class AppClipsTrampolineActivity extends Activity { finish(); } private void logScreenshotTriggeredUiEvent(@Nullable String callingPackageName) { int callingPackageUid = 0; try { callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName, APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid; } catch (NameNotFoundException e) { Log.d(TAG, "Couldn't find notes app UID " + e); } mUiEventLogger.log(SCREENSHOT_FOR_NOTE_TRIGGERED, callingPackageUid, callingPackageName); } private class AppClipsResultReceiver extends ResultReceiver { AppClipsResultReceiver(Handler handler) { Loading packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java +17 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.screenshot; import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED; import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.HardwareRenderer; Loading @@ -29,6 +31,7 @@ import android.net.Uri; import android.os.Process; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; Loading @@ -49,7 +52,8 @@ import java.util.concurrent.Executor; import javax.inject.Inject; /** A {@link ViewModel} to help with the App Clips screenshot flow. */ final class AppClipsViewModel extends ViewModel { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public final class AppClipsViewModel extends ViewModel { private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper; private final ImageExporter mImageExporter; Loading @@ -76,7 +80,8 @@ final class AppClipsViewModel extends ViewModel { } /** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */ void performScreenshot() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public void performScreenshot() { mBgExecutor.execute(() -> { Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot(); mMainExecutor.execute(() -> { Loading @@ -90,12 +95,14 @@ final class AppClipsViewModel extends ViewModel { } /** Returns a {@link LiveData} that holds the captured screenshot. */ LiveData<Bitmap> getScreenshot() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Bitmap> getScreenshot() { return mScreenshotLiveData; } /** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */ LiveData<Uri> getResultLiveData() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Uri> getResultLiveData() { return mResultLiveData; } Loading @@ -103,7 +110,8 @@ final class AppClipsViewModel extends ViewModel { * Returns a {@link LiveData} that holds the error codes for * {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}. */ LiveData<Integer> getErrorLiveData() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Integer> getErrorLiveData() { return mErrorLiveData; } Loading @@ -111,7 +119,8 @@ final class AppClipsViewModel extends ViewModel { * Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to * {@link LiveData}. */ void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) { mBgExecutor.execute(() -> { // Render the screenshot bitmap in background. Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds); Loading Loading @@ -151,7 +160,8 @@ final class AppClipsViewModel extends ViewModel { } /** Helper factory to help with injecting {@link AppClipsViewModel}. */ static final class Factory implements ViewModelProvider.Factory { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public static final class Factory implements ViewModelProvider.Factory { private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper; private final ImageExporter mImageExporter; Loading packages/SystemUI/tests/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,12 @@ android:exported="false" android:permission="com.android.systemui.permission.SELF" android:excludeFromRecents="true" /> <activity android:name="com.android.systemui.screenshot.appclips.AppClipsActivityTest$AppClipsActivityTestable" android:exported="false" android:permission="com.android.systemui.permission.SELF" android:excludeFromRecents="true" /> </application> <instrumentation android:name="android.testing.TestableInstrumentation" Loading Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +7 −1 Original line number Diff line number Diff line Loading @@ -93,7 +93,13 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "User has discarded the result of a long screenshot") SCREENSHOT_LONG_SCREENSHOT_EXIT(911), @UiEvent(doc = "A screenshot has been taken and saved to work profile") SCREENSHOT_SAVED_TO_WORK_PROFILE(1240); SCREENSHOT_SAVED_TO_WORK_PROFILE(1240), @UiEvent(doc = "Notes application triggered the screenshot for notes") SCREENSHOT_FOR_NOTE_TRIGGERED(1308), @UiEvent(doc = "User accepted the screenshot to be sent to the notes app") SCREENSHOT_FOR_NOTE_ACCEPTED(1309), @UiEvent(doc = "User cancelled the screenshot for notes app flow") SCREENSHOT_FOR_NOTE_CANCELLED(1310); private final int mId; Loading
packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java +46 −2 Original line number Diff line number Diff line Loading @@ -17,15 +17,21 @@ package com.android.systemui.screenshot; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI; import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; Loading @@ -33,15 +39,20 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import androidx.activity.ComponentActivity; import androidx.annotation.Nullable; import androidx.lifecycle.ViewModelProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLogger.UiEventEnum; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.settings.UserTracker; import javax.inject.Inject; Loading @@ -64,9 +75,15 @@ import javax.inject.Inject; * * TODO(b/267309532): Polish UI and animations. */ public final class AppClipsActivity extends ComponentActivity { public class AppClipsActivity extends ComponentActivity { private static final String TAG = AppClipsActivity.class.getSimpleName(); private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0); private final AppClipsViewModel.Factory mViewModelFactory; private final PackageManager mPackageManager; private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; private final BroadcastReceiver mBroadcastReceiver; private final IntentFilter mIntentFilter; Loading @@ -80,10 +97,17 @@ public final class AppClipsActivity extends ComponentActivity { private AppClipsViewModel mViewModel; private ResultReceiver mResultReceiver; @Nullable private String mCallingPackageName; private int mCallingPackageUid; @Inject public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory) { public AppClipsActivity(AppClipsViewModel.Factory viewModelFactory, PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger) { mViewModelFactory = viewModelFactory; mPackageManager = packageManager; mUserTracker = userTracker; mUiEventLogger = uiEventLogger; mBroadcastReceiver = new BroadcastReceiver() { @Override Loading Loading @@ -113,6 +137,7 @@ public final class AppClipsActivity extends ComponentActivity { RECEIVER_NOT_EXPORTED); Intent intent = getIntent(); setUpUiLogging(intent); mResultReceiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER, ResultReceiver.class); if (mResultReceiver == null) { setErrorThenFinish(Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED); Loading Loading @@ -169,6 +194,17 @@ public final class AppClipsActivity extends ComponentActivity { } } private void setUpUiLogging(Intent intent) { mCallingPackageName = intent.getStringExtra(EXTRA_CALLING_PACKAGE_NAME); mCallingPackageUid = 0; try { mCallingPackageUid = mPackageManager.getApplicationInfoAsUser(mCallingPackageName, APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid; } catch (NameNotFoundException e) { Log.d(TAG, "Couldn't find notes app UID " + e); } } private void setScreenshot(Bitmap screenshot) { // Set background, status and navigation bar colors as the activity is no longer // translucent. Loading Loading @@ -228,6 +264,7 @@ public final class AppClipsActivity extends ComponentActivity { data.putParcelable(EXTRA_SCREENSHOT_URI, uri); try { mResultReceiver.send(Activity.RESULT_OK, data); logUiEvent(SCREENSHOT_FOR_NOTE_ACCEPTED); } catch (Exception e) { // Do nothing. } Loading @@ -251,6 +288,9 @@ public final class AppClipsActivity extends ComponentActivity { data.putInt(Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE, errorCode); try { mResultReceiver.send(RESULT_OK, data); if (errorCode == Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED) { logUiEvent(SCREENSHOT_FOR_NOTE_CANCELLED); } } catch (Exception e) { // Do nothing. } Loading @@ -259,6 +299,10 @@ public final class AppClipsActivity extends ComponentActivity { mResultReceiver = null; } private void logUiEvent(UiEventEnum uiEvent) { mUiEventLogger.log(uiEvent, mCallingPackageUid, mCallingPackageName); } private void updateImageDimensions() { Drawable drawable = mPreview.getDrawable(); if (drawable == null) { Loading
packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java +43 −5 Original line number Diff line number Diff line Loading @@ -24,26 +24,33 @@ import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED; import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcel; import android.os.ResultReceiver; import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.settings.UserTracker; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; Loading @@ -70,15 +77,23 @@ import javax.inject.Inject; public class AppClipsTrampolineActivity extends Activity { private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName(); public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI"; public static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE"; static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER"; static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER"; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME"; private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0); private final DevicePolicyManager mDevicePolicyManager; private final FeatureFlags mFeatureFlags; private final Optional<Bubbles> mOptionalBubbles; private final NoteTaskController mNoteTaskController; private final PackageManager mPackageManager; private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; private final ResultReceiver mResultReceiver; private Intent mKillAppClipsBroadcastIntent; Loading @@ -86,11 +101,15 @@ public class AppClipsTrampolineActivity extends Activity { @Inject public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags, Optional<Bubbles> optionalBubbles, NoteTaskController noteTaskController, PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger, @Main Handler mainHandler) { mDevicePolicyManager = devicePolicyManager; mFeatureFlags = flags; mOptionalBubbles = optionalBubbles; mNoteTaskController = noteTaskController; mPackageManager = packageManager; mUserTracker = userTracker; mUiEventLogger = uiEventLogger; mResultReceiver = createResultReceiver(mainHandler); } Loading Loading @@ -138,8 +157,12 @@ public class AppClipsTrampolineActivity extends Activity { return; } Intent intent = new Intent().setComponent(componentName).addFlags( Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver); String callingPackageName = getCallingPackage(); Intent intent = new Intent().setComponent(componentName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver) .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName); try { // Start the App Clips activity. startActivity(intent); Loading @@ -150,6 +173,9 @@ public class AppClipsTrampolineActivity extends Activity { new Intent(ACTION_FINISH_FROM_TRAMPOLINE) .setComponent(componentName) .setPackage(componentName.getPackageName()); // Log successful triggering of screenshot for notes. logScreenshotTriggeredUiEvent(callingPackageName); } catch (ActivityNotFoundException e) { setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); } Loading @@ -170,6 +196,18 @@ public class AppClipsTrampolineActivity extends Activity { finish(); } private void logScreenshotTriggeredUiEvent(@Nullable String callingPackageName) { int callingPackageUid = 0; try { callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName, APPLICATION_INFO_FLAGS, mUserTracker.getUserId()).uid; } catch (NameNotFoundException e) { Log.d(TAG, "Couldn't find notes app UID " + e); } mUiEventLogger.log(SCREENSHOT_FOR_NOTE_TRIGGERED, callingPackageUid, callingPackageName); } private class AppClipsResultReceiver extends ResultReceiver { AppClipsResultReceiver(Handler handler) { Loading
packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java +17 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.systemui.screenshot; import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED; import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.HardwareRenderer; Loading @@ -29,6 +31,7 @@ import android.net.Uri; import android.os.Process; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; Loading @@ -49,7 +52,8 @@ import java.util.concurrent.Executor; import javax.inject.Inject; /** A {@link ViewModel} to help with the App Clips screenshot flow. */ final class AppClipsViewModel extends ViewModel { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public final class AppClipsViewModel extends ViewModel { private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper; private final ImageExporter mImageExporter; Loading @@ -76,7 +80,8 @@ final class AppClipsViewModel extends ViewModel { } /** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */ void performScreenshot() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public void performScreenshot() { mBgExecutor.execute(() -> { Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot(); mMainExecutor.execute(() -> { Loading @@ -90,12 +95,14 @@ final class AppClipsViewModel extends ViewModel { } /** Returns a {@link LiveData} that holds the captured screenshot. */ LiveData<Bitmap> getScreenshot() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Bitmap> getScreenshot() { return mScreenshotLiveData; } /** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */ LiveData<Uri> getResultLiveData() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Uri> getResultLiveData() { return mResultLiveData; } Loading @@ -103,7 +110,8 @@ final class AppClipsViewModel extends ViewModel { * Returns a {@link LiveData} that holds the error codes for * {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}. */ LiveData<Integer> getErrorLiveData() { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public LiveData<Integer> getErrorLiveData() { return mErrorLiveData; } Loading @@ -111,7 +119,8 @@ final class AppClipsViewModel extends ViewModel { * Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to * {@link LiveData}. */ void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) { mBgExecutor.execute(() -> { // Render the screenshot bitmap in background. Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds); Loading Loading @@ -151,7 +160,8 @@ final class AppClipsViewModel extends ViewModel { } /** Helper factory to help with injecting {@link AppClipsViewModel}. */ static final class Factory implements ViewModelProvider.Factory { @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public static final class Factory implements ViewModelProvider.Factory { private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper; private final ImageExporter mImageExporter; Loading
packages/SystemUI/tests/AndroidManifest.xml +6 −0 Original line number Diff line number Diff line Loading @@ -165,6 +165,12 @@ android:exported="false" android:permission="com.android.systemui.permission.SELF" android:excludeFromRecents="true" /> <activity android:name="com.android.systemui.screenshot.appclips.AppClipsActivityTest$AppClipsActivityTestable" android:exported="false" android:permission="com.android.systemui.permission.SELF" android:excludeFromRecents="true" /> </application> <instrumentation android:name="android.testing.TestableInstrumentation" Loading