/* * 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) { } }; } }