WallpaperPicker2壁纸设置流程
参考链接
路径:alps/packages/apps/WallpaperPicker2
初始化三个按钮,设置监听SetWallpaperDialogFragment#onCreateDialog
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
mSetHomeWallpaperButton = layout.findViewById(R.id.set_home_wallpaper_button);
mSetHomeWallpaperButton.setOnClickListener(
v -> onSetWallpaperButtonClick(WallpaperPersister.DEST_HOME_SCREEN));
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetHomeWallpaperButton,
context.getDrawable(R.drawable.ic_home_24px));
mSetLockWallpaperButton = layout.findViewById(R.id.set_lock_wallpaper_button);
mSetLockWallpaperButton.setOnClickListener(
v -> onSetWallpaperButtonClick(WallpaperPersister.DEST_LOCK_SCREEN));
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetLockWallpaperButton,
context.getDrawable(R.drawable.ic_lock_outline_24px));
mSetBothWallpaperButton = layout.findViewById(R.id.set_both_wallpaper_button);
mSetBothWallpaperButton.setOnClickListener(
v -> onSetWallpaperButtonClick(WallpaperPersister.DEST_BOTH));
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetBothWallpaperButton,
context.getDrawable(R.drawable.ic_smartphone_24px));
...
}
三个按钮事件,触发回调 SetWallpaperDialogFragment#onSetWallpaperButtonClick
private void onSetWallpaperButtonClick(int destination) {
mWithItemSelected = true;
mListener.onSet(destination); //回调
dismiss();
}
回调实现PreviewFragment#onSet
public void onSet(int destination) {
setCurrentWallpaper(destination);
}
继续查看setCurrentWallpaper
/**
* Sets current wallpaper to the device based on current zoom and scroll state.
*
* @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
*/
protected abstract void setCurrentWallpaper(@Destination int destination);
有两处实现的位置:
1.继续追踪实现抽象方法的位置ImagePreviewFragment#setCurrentWallpaper
@Override
protected void setCurrentWallpaper(@Destination int destination) {
if (getActivity() == null) {
Log.w(TAG, "The activity is null, so don't set wallpaper!");
return;
}
//使用父类PreviewFragment中初始化的>>==mWallpaperSetter==<<
>>==>>++mWallpaperSetter++<<==<<.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
destination, mFullResImageView.getScale(), calculateCropRect(),
new SetWallpaperCallback() {
@Override
public void onSuccess(WallpaperInfo wallpaperInfo) {
finishActivityWithResultOk();
}
@Override
public void onError(@Nullable Throwable throwable) {
showSetWallpaperErrorDialog(destination);
}
});
}
2.继续追踪实现抽象方法的位置PicturePreviewFragment#setCurrentWallpaper
@Override
protected void setCurrentWallpaper(int destination) {
Rect cropRect = new Rect();
mCropScale = calculateCropRectAndScale(cropRect);
//使用父类PreviewFragment中初始化的>>==mWallpaperSetter==<<
>>==>>++mWallpaperSetter++<<==<<.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
destination, mCropScale, cropRect, new WallpaperPersister.SetWallpaperCallback() {
@Override
public void onSuccess(WallpaperInfo wallpaperInfo) {
finishActivityWithResultOk();
}
@Override
public void onError(@Nullable Throwable throwable) {
showSetWallpaperErrorDialog(destination);
}
});
}
具体是使用哪个类中的setCurrentWallpaper方法呢,通过查看在实例化PreviewFragment地方,结果为通过三元表达式判断是哪个子类,根据实例化的子类的类型判断是执行了哪个setCurrentWallpaper方法
public static PreviewFragment newInstance(
WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) {
boolean isLive = wallpaperInfo instanceof LiveWallpaperInfo;
Bundle args = new Bundle();
args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
args.putInt(ARG_PREVIEW_MODE, mode);
args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
>>==PreviewFragment fragment = isLive ? new LivePreviewFragment() :
Flags.SPRD_WALLPAPER_STYLE_OPTION_ENABLED ?
new PicturePreviewFragment() : new ImagePreviewFragment();==<<
fragment.setArguments(args);
return fragment;
}
Flags继承自BaseFlags
abstract class BaseFlags {
public static boolean skipDailyWallpaperButtonEnabled = true;
public static boolean desktopUiEnabled = false;
public static boolean dynamicStartRotationTileEnabled = true;
public static boolean stagingBackdropContentEnabled = false;
public static boolean performanceMonitoringEnabled = true;
public static final boolean SPRD_ENABLE_LOCK_WALLPAPER =
SystemPropertiesUtils.getBoolean("ro.lockwallpaper.enable", true);
public static boolean SPRD_STABLE_WALLPAPER =
SystemPropertiesUtils.getBoolean("ro.wallpaper.stable", false);
public static boolean SPRD_WALLPAPER_STYLE_OPTION_ENABLED =
!SPRD_STABLE_WALLPAPER &&
SystemPropertiesUtils.getBoolean("ro.wallpaper.style.enable", true);
public static final boolean DEBUG =
SystemPropertiesUtils.getBoolean("persist.sys.wallpaper.debug", false);
}
查看WallpaperSetter类中的setCurrentWallpaper实现方法
/**
* Sets current wallpaper to the device with the minimum scale to fit the screen size.
*
* @param containerActivity main Activity that owns the current fragment
* @param wallpaper info for the actual wallpaper to set
* @param destination the wallpaper destination i.e. home vs. lockscreen vs. both.
* @param callback optional callback to be notified when the wallpaper is set.
*/
public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
@Destination final int destination,
@Nullable SetWallpaperCallback callback) {
Asset wallpaperAsset = wallpaper.getAsset(containerActivity.getApplicationContext());
wallpaperAsset.decodeRawDimensions(containerActivity, dimensions -> {
if (dimensions == null) {
Log.e(TAG, "Raw wallpaper's dimensions are null");
return;
}
Display defaultDisplay = containerActivity.getWindowManager().getDefaultDisplay();
Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay);
Rect visibleRawWallpaperRect =
WallpaperCropUtils.calculateVisibleRect(dimensions, screenSize);
float wallpaperScale = WallpaperCropUtils.calculateMinZoom(dimensions, screenSize);
Rect cropRect = WallpaperCropUtils.calculateCropRect(
containerActivity.getApplicationContext(), defaultDisplay,
dimensions, visibleRawWallpaperRect, wallpaperScale);
setCurrentWallpaper(containerActivity, wallpaper, wallpaperAsset, destination,
wallpaperScale, cropRect, callback);
});
}
/**
* Sets current wallpaper to the device based on current zoom and scroll state.
*
* @param containerActivity main Activity that owns the current fragment
* @param wallpaper info for the actual wallpaper to set
* @param wallpaperAsset Wallpaper asset from which to retrieve image data.
* @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
* @param wallpaperScale Scaling factor applied to the source image before setting the
* wallpaper to the device.
* @param cropRect Desired crop area of the wallpaper in post-scale units. If null, then the
* wallpaper image will be set without any scaling or cropping.
* @param callback optional callback to be notified when the wallpaper is set.
*/
public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
@Nullable Asset wallpaperAsset, @Destination final int destination,
float wallpaperScale, @Nullable Rect cropRect,
@Nullable SetWallpaperCallback callback) {
if (wallpaper instanceof LiveWallpaperInfo) {
setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination,
callback);
return;
}
mPreferences.setPendingWallpaperSetStatus(
WallpaperPreferences.WALLPAPER_SET_PENDING);
// Save current screen rotation so we can temporarily disable rotation while setting the
// wallpaper and restore after setting the wallpaper finishes.
saveAndLockScreenOrientationIfNeeded(containerActivity);
// Clear MosaicView tiles and Glide's cache and pools to reclaim memory for final cropped
// bitmap.
Glide.get(containerActivity).clearMemory();
// ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
// causes Espresso to hang once the dialog is shown.
if (!mTestingModeEnabled && !containerActivity.isFinishing()) {
int themeResId = (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
? R.style.ProgressDialogThemePreL : R.style.LightDialogTheme;
mProgressDialog = new ProgressDialog(containerActivity, themeResId);
mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
mProgressDialog.setMessage(containerActivity.getString(
R.string.set_wallpaper_progress_message));
mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
mProgressDialog.show();
}
>>==mWallpaperPersister.setIndividualWallpaper(==<<
wallpaper, wallpaperAsset, cropRect,
wallpaperScale, destination, new SetWallpaperCallback() {
@Override
public void onSuccess(WallpaperInfo wallpaperInfo) {
onWallpaperApplied(wallpaper, containerActivity);
if (callback != null) {
callback.onSuccess(wallpaper);
}
}
@Override
public void onError(Throwable throwable) {
onWallpaperApplyError(throwable, containerActivity);
if (callback != null) {
callback.onError(throwable);
}
}
});
}
//设置动态壁纸
public void setCurrentLiveWallpaper(Activity activity, LiveWallpaperInfo wallpaper,
@Destination final int destination, @Nullable SetWallpaperCallback callback) {
try {
// Save current screen rotation so we can temporarily disable rotation while setting the
// wallpaper and restore after setting the wallpaper finishes.
saveAndLockScreenOrientationIfNeeded(activity);
if (destination == WallpaperPersister.DEST_LOCK_SCREEN) {
throw new IllegalArgumentException(
"Live wallpaper cannot be applied on lock screen only");
}
WallpaperManager wallpaperManager = WallpaperManager.getInstance(activity);
wallpaperManager.setWallpaperComponent(
wallpaper.getWallpaperComponent().getComponent());
wallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */);
wallpaperManager.setWallpaperOffsets(
activity.getWindow().getDecorView().getRootView().getWindowToken(),
0.5f /* xOffset */, 0.0f /* yOffset */);
if (destination == WallpaperPersister.DEST_BOTH) {
wallpaperManager.clear(WallpaperManager.FLAG_LOCK);
}
onWallpaperApplied(wallpaper, activity);
if (callback != null) {
callback.onSuccess(wallpaper);
}
} catch (RuntimeException | IOException e) {
onWallpaperApplyError(e, activity);
if (callback != null) {
callback.onError(e);
}
}
}
之后调用WallpaperPersister接口中的setIndividualWallpaper方法,具体实现类为DefaultWallpaperPersister
@Override
public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset,
@Nullable Rect cropRect, float scale, @Destination final int destination,
final SetWallpaperCallback callback) {
// Set wallpaper without downscaling directly from an input stream if there's no crop rect
// specified by the caller and the asset is streamable.
if (cropRect == null && asset instanceof StreamableAsset) {
((StreamableAsset) asset).fetchInputStream(new StreamReceiver() {
@Override
public void onInputStreamOpened(@Nullable InputStream inputStream) {
if (inputStream == null) {
callback.onError(null /* throwable */);
return;
}
setIndividualWallpaper(wallpaper, inputStream, destination, callback);
}
});
return;
}
// If no crop rect is specified but the wallpaper asset is not streamable, then fall back to
// using the device's display size.
if (cropRect == null) {
Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
@Override
public void onBitmapDecoded(@Nullable Bitmap bitmap) {
if (bitmap == null) {
callback.onError(null /* throwable */);
return;
}
setIndividualWallpaper(wallpaper, bitmap, destination, callback);
}
});
return;
}
BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
bitmapCropper.cropAndScaleBitmap(asset, scale, cropRect, new Callback() {
@Override
public void onBitmapCropped(Bitmap croppedBitmap) {
setIndividualWallpaper(wallpaper, croppedBitmap, destination, callback);
}
@Override
public void onError(@Nullable Throwable e) {
callback.onError(e);
}
});
}
接着调用不同参数的setIndividualWallpaper
/**
* Sets a static individual wallpaper to the system via the WallpaperManager.
*
* @param wallpaper Wallpaper model object.
* @param croppedBitmap Bitmap representing the individual wallpaper image.
* @param destination The destination - where to set the wallpaper to.
* @param callback Called once the wallpaper was set or if an error occurred.
*/
private void setIndividualWallpaper(WallpaperInfo wallpaper, Bitmap croppedBitmap,
@Destination int destination, SetWallpaperCallback callback) {
SetWallpaperTask setWallpaperTask =
new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
setWallpaperTask.execute();
}
继续追踪SetWallpaperTask,找到其中开启线程设置壁纸的位置
@Override
protected Boolean doInBackground(Void... unused) {
int whichWallpaper;
if (mDestination == DEST_HOME_SCREEN) {
whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM;
} else if (mDestination == DEST_LOCK_SCREEN) {
whichWallpaper = WallpaperManagerCompat.FLAG_LOCK;
} else { // DEST_BOTH
whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM
| WallpaperManagerCompat.FLAG_LOCK;
}
boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(
mAppContext);
boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED;
final int wallpaperId;
if (mBitmap != null) {
// Apply fill or stretch transformations on mBitmap if necessary.
if (mFillSize != null) {
mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize);
}
if (mStretchSize != null) {
mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y,
true);
}
wallpaperId = >>==setBitmapToWallpaperManagerCompat==<<(mBitmap, allowBackup,
whichWallpaper);
} else if (mInputStream != null) {
wallpaperId = >>==setStreamToWallpaperManagerCompat==<<(mInputStream, allowBackup,
whichWallpaper);
} else {
Log.e(TAG,
"Both the wallpaper bitmap and input stream are null so we're unable to "
+ "set any "
+ "kind of wallpaper here.");
wallpaperId = 0;
}
if (wallpaperId > 0) {
if (mDestination == DEST_HOME_SCREEN
&& mWallpaperPreferences.getWallpaperPresentationMode()
== WallpaperPreferences.PRESENTATION_MODE_ROTATING
&& !wasLockWallpaperSet
&& BuildCompat.isAtLeastN()) {
copyRotatingWallpaperToLock();
}
setImageWallpaperMetadata(mDestination, wallpaperId);
return true;
} else {
return false;
}
}
之后调用setBitmapToWallpaperManagerCompat/setStreamToWallpaperManagerCompat方法设置壁纸
private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
int whichWallpaper) {
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
try {
byte[] outByteArray = tmpOut.toByteArray();
return mWallpaperManagerCompat.setStream(
new ByteArrayInputStream(outByteArray),
null /* visibleCropHint */,
allowBackup,
whichWallpaper);
} catch (IOException e) {
Log.e(TAG, "unable to write stream to wallpaper manager");
return 0;
}
} else {
Log.e(TAG, "unable to compress wallpaper");
try {
return mWallpaperManagerCompat.setBitmap(
wallpaperBitmap,
null /* visibleCropHint */,
allowBackup,
whichWallpaper);
} catch (IOException e) {
Log.e(TAG, "unable to set wallpaper");
return 0;
}
}
}
private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,
int whichWallpaper) {
try {
return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup,
whichWallpaper);
} catch (IOException e) {
return 0;
}
}
接着会调用mWallpaperManagerCompat.setBitmap/setStream方法,WallpaperManagerCompatVN : WallpaperManagerCompatV16 : WallpaperManagerCompat(继承关系),最终会调用WallpaperManager的setStream或setBitmap方法.所以追踪到/alps/frameworks/base/core/java/android/app/WallpaperManager.java中.
/**
* Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user
* id. If the user id doesn't match the user id the process is running under, calling this
* requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
boolean allowBackup, @SetWallpaperFlags int which, int userId)
throws IOException {
validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
final Bundle result = new Bundle();
final WallpaperSetCompletion completion = new WallpaperSetCompletion();
try {
ParcelFileDescriptor fd = >>++>>==sGlobals.mService.setWallpaper==<<++<<(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
result, which, completion, userId);
if (fd != null) {
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
completion.waitForCompletion();
} finally {
IoUtils.closeQuietly(fos);
}
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
/**
* Version of {@link #setStream(InputStream, Rect, boolean)} that allows the caller
* to specify which of the supported wallpaper categories to set.
*
* @param bitmapData A stream containing the raw data to install as a wallpaper. This
* data can be in any format handled by {@link BitmapRegionDecoder}.
* @param visibleCropHint The rectangular subregion of the streamed image that should be
* displayed as wallpaper. Passing {@code null} for this parameter means that
* the full image should be displayed if possible given the image's and device's
* aspect ratios, etc.
* @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
* @return An integer ID assigned to the newly active wallpaper; or zero on failure.
*
* @see #getWallpaperId(int)
* @see #FLAG_LOCK
* @see #FLAG_SYSTEM
*
* @throws IOException
*/
@RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
public int setStream(InputStream bitmapData, Rect visibleCropHint,
boolean allowBackup, @SetWallpaperFlags int which)
throws IOException {
validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
throw new RuntimeException(new DeadSystemException());
}
final Bundle result = new Bundle();
final WallpaperSetCompletion completion = new WallpaperSetCompletion();
try {
ParcelFileDescriptor fd = >>++>>==sGlobals.mService.setWallpaper==<<++<<(null,
mContext.getOpPackageName(), visibleCropHint, allowBackup,
result, which, completion, mContext.getUserId());
if (fd != null) {
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
copyStreamToWallpaperFile(bitmapData, fos);
fos.close();
completion.waitForCompletion();
} finally {
IoUtils.closeQuietly(fos);
}
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
通过AIDL的方式绑定服务,使用setWallpaper方法设置壁纸,绑定的服务为WallpaperManagerService,从而找到/frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
Rect cropHint, boolean allowBackup, Bundle extras, int which,
IWallpaperManagerCallback completion, int userId) {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
checkPermission(android.Manifest.permission.SET_WALLPAPER);
if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
final String msg = "Must specify a valid wallpaper category to set";
Slog.e(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
return null;
}
// "null" means the no-op crop, preserving the full input image
if (cropHint == null) {
cropHint = new Rect(0, 0, 0, 0);
} else {
if (cropHint.width() < 0 || cropHint.height() < 0
|| cropHint.left < 0
|| cropHint.top < 0) {
throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
}
}
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper;
/* If we're setting system but not lock, and lock is currently sharing the system
* wallpaper, we need to migrate that image over to being lock-only before
* the caller here writes new bitmap data.
*/
if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
migrateSystemToLockWallpaperLocked(userId);
}
wallpaper = getWallpaperSafeLocked(userId, which);
final long ident = Binder.clearCallingIdentity();
try {
ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
if (pfd != null) {
wallpaper.imageWallpaperPending = true;
wallpaper.whichPending = which;
wallpaper.setComplete = completion;
wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup;
}
return pfd;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
追到头疼…就到这了 QWQ