Provide a sample of multi-display wallpaper.
Some key events to build a wallpaper which can support multiple display. - The rendering or event handler of each engine must be independent. - Set supportsMultipleDisplays attribute to true. - Use Engine#getDisplayContext to load resource if needed. Bug: 129696884 Test: launch wallpaper and check on multi display. Change-Id: I3c60caba921e0af3d5f681ed318a543328e3375a
This commit is contained in:
@@ -18,6 +18,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.multidisplay">
|
||||
|
||||
<uses-sdk android:minSdkVersion="17" />
|
||||
|
||||
<application
|
||||
android:label="@string/app_name">
|
||||
<activity
|
||||
@@ -34,5 +36,20 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".wallpaper.SampleWallpaper"
|
||||
android:permission="android.permission.BIND_WALLPAPER">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.wallpaper.WallpaperService">
|
||||
</action>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.service.wallpaper"
|
||||
android:resource="@xml/wallpaper">
|
||||
</meta-data>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
||||
BIN
samples/MultiDisplay/res/drawable-nodpi/res_image.png
Normal file
BIN
samples/MultiDisplay/res/drawable-nodpi/res_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
samples/MultiDisplay/res/drawable-sw320dp/res_image.png
Normal file
BIN
samples/MultiDisplay/res/drawable-sw320dp/res_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
samples/MultiDisplay/res/drawable-sw480dp/res_image.png
Normal file
BIN
samples/MultiDisplay/res/drawable-sw480dp/res_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
samples/MultiDisplay/res/drawable-sw640dp/res_image.png
Normal file
BIN
samples/MultiDisplay/res/drawable-sw640dp/res_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
samples/MultiDisplay/res/drawable-sw960dp/res_image.png
Normal file
BIN
samples/MultiDisplay/res/drawable-sw960dp/res_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
@@ -23,4 +23,5 @@
|
||||
<string name="add_app_shortcut">Add app shortcut</string>
|
||||
<string name="set_wallpaper">Set wallpaper</string>
|
||||
<string name="new_instance">Request launch in new instance</string>
|
||||
<string name="wallpaper_description">Sample multidisplay wallpaper</string>
|
||||
</resources>
|
||||
|
||||
21
samples/MultiDisplay/res/xml/wallpaper.xml
Normal file
21
samples/MultiDisplay/res/xml/wallpaper.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<wallpaper
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:description="@string/wallpaper_description"
|
||||
android:supportsMultipleDisplays="true"
|
||||
/>
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.example.android.multidisplay.wallpaper;
|
||||
|
||||
|
||||
import android.app.WallpaperColors;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.os.Handler;
|
||||
import android.service.wallpaper.WallpaperService;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.WindowManager;
|
||||
import java.util.Random;
|
||||
import com.example.android.multidisplay.R;
|
||||
|
||||
public class SampleWallpaper extends WallpaperService {
|
||||
|
||||
@Override
|
||||
public Engine onCreateEngine() {
|
||||
return new MySampleEngine();
|
||||
}
|
||||
|
||||
private class MySampleEngine extends Engine {
|
||||
private boolean mVisible = false;
|
||||
private DisplayMetrics mDisplayMetrics;
|
||||
private Display mDisplay;
|
||||
private Paint mPaint = new Paint();
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final Runnable mDrawRunner = this::draw;
|
||||
|
||||
private String mShowingText;
|
||||
private final Rect mTextBounds = new Rect();
|
||||
|
||||
private Bitmap mTipImage;
|
||||
|
||||
private final Point mCircleShift = new Point();
|
||||
private final Point mCirclePosition = new Point();
|
||||
private float mCircleRadioShift;
|
||||
private final float MaxCircleRadioShift = 6f;
|
||||
private boolean mRadioRevert = false;
|
||||
|
||||
private int mBackgroundColor = Color.BLACK;
|
||||
private int mPaintColor = Color.WHITE;
|
||||
|
||||
private boolean mCircleDirectionX = false;
|
||||
private boolean mCircleDirectionY = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(SurfaceHolder surfaceHolder) {
|
||||
initDisplay();
|
||||
updateDisplay();
|
||||
initPaint();
|
||||
genNewShift();
|
||||
genNewColor();
|
||||
mHandler.post(mDrawRunner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
final DisplayManager dm = getSystemService(DisplayManager.class);
|
||||
if (dm != null) {
|
||||
dm.unregisterDisplayListener(mDisplayListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WallpaperColors onComputeColors() {
|
||||
super.onComputeColors();
|
||||
ColorDrawable drawable = new ColorDrawable(mBackgroundColor);
|
||||
drawable.setBounds(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.widthPixels);
|
||||
return WallpaperColors.fromDrawable(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityChanged(boolean visible) {
|
||||
mVisible = visible;
|
||||
if (visible) {
|
||||
mHandler.post(mDrawRunner);
|
||||
} else {
|
||||
mHandler.removeCallbacks(mDrawRunner);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mCirclePosition.x = (int) event.getX();
|
||||
mCirclePosition.y = (int) event.getY();
|
||||
invertCircleDirectionIfNeeded();
|
||||
}
|
||||
super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceDestroyed(SurfaceHolder holder) {
|
||||
super.onSurfaceDestroyed(holder);
|
||||
mVisible = false;
|
||||
mHandler.removeCallbacks(mDrawRunner);
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
SurfaceHolder holder = getSurfaceHolder();
|
||||
Canvas canvas = null;
|
||||
float centerX = (float) mDisplayMetrics.widthPixels/2;
|
||||
float centerY = (float) mDisplayMetrics.heightPixels/2;
|
||||
|
||||
updateShift();
|
||||
invertCircleDirectionIfNeeded();
|
||||
|
||||
try {
|
||||
canvas = holder.lockCanvas();
|
||||
if (canvas != null) {
|
||||
canvas.drawColor(mBackgroundColor);
|
||||
if (mTipImage != null) {
|
||||
canvas.drawBitmap(mTipImage, 0, 0, mPaint);
|
||||
}
|
||||
canvas.drawText(mShowingText, centerX - mTextBounds.exactCenterX(),
|
||||
centerY - mTextBounds.exactCenterY(), mPaint);
|
||||
|
||||
canvas.drawCircle(mCirclePosition.x, mCirclePosition.y,
|
||||
20.0f + mCircleRadioShift, mPaint);
|
||||
}
|
||||
} finally {
|
||||
if (canvas != null)
|
||||
holder.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
mHandler.removeCallbacks(mDrawRunner);
|
||||
if (mVisible) {
|
||||
mHandler.postDelayed(mDrawRunner, 40);
|
||||
}
|
||||
}
|
||||
|
||||
private void invertCircleDirectionIfNeeded() {
|
||||
boolean invertX = mCirclePosition.x < 0
|
||||
|| mCirclePosition.x > mDisplayMetrics.widthPixels;
|
||||
boolean invertY = mCirclePosition.y < 0
|
||||
|| mCirclePosition.y > mDisplayMetrics.heightPixels;
|
||||
|
||||
if (!invertX && !invertY) return;
|
||||
|
||||
if (invertX) {
|
||||
mCircleDirectionX = mCirclePosition.x < 0;
|
||||
}
|
||||
if (invertY) {
|
||||
mCircleDirectionY = mCirclePosition.y < 0;
|
||||
}
|
||||
|
||||
genNewShift();
|
||||
genNewColor();
|
||||
}
|
||||
|
||||
private void updateShift() {
|
||||
mCirclePosition.x = mCircleDirectionX
|
||||
? mCirclePosition.x + mCircleShift.x
|
||||
: mCirclePosition.x - mCircleShift.x;
|
||||
mCirclePosition.y = mCircleDirectionY
|
||||
? mCirclePosition.y + mCircleShift.y
|
||||
: mCirclePosition.y - mCircleShift.y;
|
||||
|
||||
mCircleRadioShift = mRadioRevert ? mCircleRadioShift + 1f : mCircleRadioShift - 1f;
|
||||
if (Math.abs(mCircleRadioShift) > MaxCircleRadioShift) {
|
||||
mRadioRevert = !mRadioRevert;
|
||||
}
|
||||
}
|
||||
|
||||
private void genNewShift() {
|
||||
Random random = new Random();
|
||||
mCircleShift.x = Math.abs(random.nextInt(5));
|
||||
mCircleShift.y = Math.abs(5 - mCircleShift.x);
|
||||
}
|
||||
|
||||
private void genNewColor() {
|
||||
final Random random = new Random();
|
||||
int br = random.nextInt(256);
|
||||
int bg = random.nextInt(256);
|
||||
int bb = random.nextInt(256);
|
||||
|
||||
// Keep some contrast...
|
||||
int pg = Math.abs(bg - 128);
|
||||
int pr = Math.abs(br - 128);
|
||||
int pb = Math.abs(bb - 128);
|
||||
mBackgroundColor = Color.argb(255, br, bg, bb);
|
||||
mPaintColor = Color.argb(255, pr, pg, pb);
|
||||
mPaint.setColor(mPaintColor);
|
||||
}
|
||||
|
||||
private void initDisplay() {
|
||||
// If we want to get display, use getDisplayContext().getSystemService so the
|
||||
// WindowManager is created for this context.
|
||||
final WindowManager wm = getDisplayContext().getSystemService(WindowManager.class);
|
||||
if (wm != null) {
|
||||
mDisplay = wm.getDefaultDisplay();
|
||||
}
|
||||
final DisplayManager dm = getSystemService(DisplayManager.class);
|
||||
if (dm != null) {
|
||||
dm.registerDisplayListener(mDisplayListener, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplay() {
|
||||
// Use getDisplayContext() to get the context for current display.
|
||||
mDisplayMetrics = getDisplayContext().getResources().getDisplayMetrics();
|
||||
mCirclePosition.x = mDisplayMetrics.widthPixels/2;
|
||||
mCirclePosition.y = mDisplayMetrics.heightPixels/2 + 60;
|
||||
|
||||
mShowingText = "densityDpi= " + mDisplayMetrics.densityDpi;
|
||||
if (mTipImage != null) {
|
||||
mTipImage.recycle();
|
||||
mTipImage = null;
|
||||
}
|
||||
mTipImage = BitmapFactory
|
||||
.decodeResource(getDisplayContext().getResources(), R.drawable.res_image);
|
||||
mPaint.getTextBounds(mShowingText, 0, mShowingText.length(), mTextBounds);
|
||||
}
|
||||
|
||||
public MySampleEngine() {
|
||||
|
||||
}
|
||||
|
||||
private void initPaint() {
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeWidth(1f);
|
||||
mPaint.setTextSize(50f);
|
||||
}
|
||||
|
||||
// Use DisplayListener to know display changed.
|
||||
private final DisplayListener mDisplayListener = new DisplayListener() {
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
if (mDisplay.getDisplayId() == displayId) {
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
// handle here or wait onDestroy
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user