-----------------------------------------------------------------------------------------------------------------------------------------------------
android10的壁纸显示流程已经和之前的不一样了,我也查了一些资料,然后结合自己看代码,最后找到了显示壁纸的地方了。只是静态壁纸。
packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
不管是去设置壁纸还是当你休眠唤醒,都会走这里壁纸。
ImageWallpaperRenderer这个在ImageWallpaper中有调用,ImageWallpaper也需要一起看看。
packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
-----------------------------------------------------------忽略,这个是壁纸显示相关的-----------------------------------------------------------------------
Wallpapers
长按桌面,选择wallpapers去设置壁纸,可以设置主屏,也可以设置锁屏,也可以同时设置。
/packages/apps/WallpaperPicker2/ 代码在这里。这里分析一下点了设置后的流程。
从桌面长按,然后选择wallpapers,然后选择一张图片,然后点击设置,这时候会有个对话框出来,让你选择设置的类型。Home screen / Lock screen / Home and Lock screen
这个avtivity代码在
/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewActivity.java
我们主要看这个对话框的代码,这个对话框的代码在
/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/SetWallpaperDialogFragment.java
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
Context context = getContext();
@SuppressWarnings("RestrictTo")
View layout =
View.inflate(
new ContextThemeWrapper(getActivity(), R.style.LightDialogTheme),
R.layout.dialog_set_wallpaper,
null);
AlertDialog dialog = new AlertDialog.Builder(context, R.style.LightDialogTheme)
.setTitle(mTitleResId)
.setView(layout)
.create();
mSetHomeWallpaperButton = layout.findViewById(R.id.set_home_wallpaper_button);
mSetHomeWallpaperButton.setOnClickListener(v -> {
mListener.onSet(WallpaperPersister.DEST_HOME_SCREEN);
dismiss();
});
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetHomeWallpaperButton,
context.getDrawable(R.drawable.ic_home_24px));
mSetLockWallpaperButton = layout.findViewById(R.id.set_lock_wallpaper_button);
mSetLockWallpaperButton.setOnClickListener(v -> {
mListener.onSet(WallpaperPersister.DEST_LOCK_SCREEN);
dismiss();
});
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetLockWallpaperButton,
context.getDrawable(R.drawable.ic_lock_outline_24px));
mSetBothWallpaperButton = layout.findViewById(R.id.set_both_wallpaper_button);
mSetBothWallpaperButton.setOnClickListener(v -> {
mListener.onSet(WallpaperPersister.DEST_BOTH);
dismiss();
});
ButtonDrawableSetterCompat.setDrawableToButtonStart(
mSetBothWallpaperButton,
context.getDrawable(R.drawable.ic_smartphone_24px));
updateButtonsVisibility();
return dialog;
}
可以看到这里面就有3个button,对应的就是screen / Lock screen / Home and Lock screen,点击后就会调用onSet这个接口,下面就去追踪这个接口的实现。
这个接口实现的地方在
/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewFragment.java
@Override
public void onSet(int destination) {
setCurrentWallpaper(destination);
}
/**
* 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.
*/
private void setCurrentWallpaper(@Destination final int destination) {
mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
destination, mFullResImageView.getScale(), calculateCropRect(),
new SetWallpaperCallback() {
@Override
public void onSuccess() {
finishActivityWithResultOk();
}
@Override
public void onError(@Nullable Throwable throwable) {
showSetWallpaperErrorDialog(destination);
}
});
}
然后调用setCurrentWallpaper去设置壁纸。代码在
/packages/apps/WallpaperPicker2/src/com/android/module/WallpaperSetter.java
/**
* 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,
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.
saveAndLockScreenOrientation(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();
}
Log.i("a", "WallpaperSetter destination "+destination);
mWallpaperPersister.setIndividualWallpaper(
wallpaper, wallpaperAsset, cropRect,
wallpaperScale, destination, new SetWallpaperCallback() {
@Override
public void onSuccess() {
onWallpaperApplied(wallpaper, containerActivity);
if (callback != null) {
callback.onSuccess();
}
}
@Override
public void onError(Throwable throwable) {
onWallpaperApplyError(throwable, containerActivity);
if (callback != null) {
callback.onError(throwable);
}
}
});
}
调用WallpaperPersister接口的setIndividualWallpaper方法去设置壁纸,实现的地方在。
/packages/apps/WallpaperPicker2/src/com/android/module/DefaultWallpaperPersister.java
@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 stream to the system via the WallpaperManager.
*
* @param wallpaper Wallpaper model object.
* @param inputStream JPEG or PNG stream of wallpaper image's bytes.
* @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, InputStream inputStream,
@Destination int destination, SetWallpaperCallback callback) {
SetWallpaperTask setWallpaperTask =
new SetWallpaperTask(wallpaper, inputStream, 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;
}
Log.i("a", "DefaultWallpaperPersister mDestination whichWallpaper "+mDestination+" "+whichWallpaper);
// NOTE: The rotating wallpaper component must be determined here, _before_ actually setting
// the bitmap/stream on WallpaperManagerCompat, to ensure that the
// RotatingWallpaperComponentChecker is doing its check while rotation is still enabled.
// E.g., if "live wallpaper" is the component, then it needs to check while live wallpaper is
// still set as the active wallpaper on the device. Otherwise, the checker would see a static
// wallpaper is currently set and it would return the wrong value.
@RotatingWallpaperComponent int currentRotatingWallpaperComponent =
mRotatingWallpaperComponentChecker.getCurrentRotatingWallpaperComponent(mAppContext);
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()) {
Log.i("a", "DefaultWallpaperPersister 883");
copyRotatingWallpaperToLock(currentRotatingWallpaperComponent);
}
setImageWallpaperMetadata(mDestination, wallpaperId);
Log.i("a", "DefaultWallpaperPersister 887 wallpaperId "+wallpaperId);
return true;
} else {
return false;
}
}
然后会走setBitmapToWallpaperManagerCompat,我这边设置的是图片,所以根据log,代码走这了。
/**
* Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}.
*
* @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of
* Android, otherwise on pre-N versions of Android will return a positive integer when the
* operation was successful and zero if the operation encountered an error.
*/
private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
int whichWallpaper) {
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
try {
Log.i("a", "DefaultWallpaperPersister setBitmapToWallpaperManagerCompat 645 whichWallpaper "+whichWallpaper);
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");
Log.i("a", "DefaultWallpaperPersister setBitmapToWallpaperManagerCompat 658");
try {
return mWallpaperManagerCompat.setBitmap(
wallpaperBitmap,
null /* visibleCropHint */,
allowBackup,
whichWallpaper);
} catch (IOException e) {
Log.e(TAG, "unable to set wallpaper");
return 0;
}
}
}
然后会走接口WallpaperManagerCompat的setStream 或者setBitmap。
这里接口实现的地方我不确定在哪,
/packages/apps/WallpaperPicker2/src/com/android/compat/WallpaperManagerCompatV16.java
/packages/apps/WallpaperPicker2/src/com/android/compat/WallpaperManagerCompatVN.java
但是他们最后都会调用WallpaperManager的setStream和setBitmap。我们追踪WallpaperManager就行了。我追的是setBitmap。
/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);
}
这里调用sGlobals.mService.setWallpaper去设置壁纸。这个mService就是IWallpaperManager,这边是用的AIDL的方法。所以我们去找WallpaperManagerService就能找到实现的地方,
/frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@Override
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.isEmpty()
|| cropHint.left < 0
|| cropHint.top < 0) {
throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
}
}
synchronized (mLock) {
Slog.v("a", "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("a", "Migrating system->lock to preserve");
migrateSystemToLockWallpaperLocked(userId);
}
wallpaper = getWallpaperSafeLocked(userId, which);
Slog.v("a", "getWallpaperSafeLocked wallpaper.whichPending " + wallpaper.whichPending);
final long ident = Binder.clearCallingIdentity();
try {
ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
if (pfd != null) {
Slog.v("a", "updateWallpaperBitmapLocked wallpaper.whichPending " + wallpaper.whichPending);
wallpaper.imageWallpaperPending = true;
wallpaper.whichPending = which;
wallpaper.setComplete = completion;
wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup;
}
return pfd;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
注意一下migrateSystemToLockWallpaperLocked,如果你没设置过lock,并且你现在只去设置home,那么它会先把原先的home壁纸拷贝到lock壁纸,然后再去设置home。(如果这个migrateSystemToLockWallpaperLocked执行没成功的话,那么你会发现你设置了home壁纸,但是lock壁纸也变了。只有第一次有这种情况,之后设置不会,我遇到的。出现Can't migrate system wallpaper:异常了。)
下面没有继续追了。