mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
pt 1 in reviving the android build (copied from pm 28a1, wish me luck)
This commit is contained in:
parent
efa9662725
commit
d7788a6d6d
4249 changed files with 468189 additions and 0 deletions
|
|
@ -0,0 +1,455 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.lwt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.WindowUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
|
||||
public class LightweightTheme implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoLightweightTheme";
|
||||
|
||||
private static final String PREFS_URL = "lightweightTheme.headerURL";
|
||||
private static final String PREFS_COLOR = "lightweightTheme.color";
|
||||
|
||||
private static final String ASSETS_PREFIX = "resource://android/assets/";
|
||||
|
||||
private final Application mApplication;
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private int mColor;
|
||||
private boolean mIsLight;
|
||||
|
||||
public static interface OnChangeListener {
|
||||
// The View should change its background/text color.
|
||||
public void onLightweightThemeChanged();
|
||||
|
||||
// The View should reset to its default background/text color.
|
||||
public void onLightweightThemeReset();
|
||||
}
|
||||
|
||||
private final List<OnChangeListener> mListeners;
|
||||
|
||||
class LightweightThemeRunnable implements Runnable {
|
||||
private String mHeaderURL;
|
||||
private String mColor;
|
||||
|
||||
private String mSavedURL;
|
||||
private String mSavedColor;
|
||||
|
||||
LightweightThemeRunnable() {
|
||||
}
|
||||
|
||||
LightweightThemeRunnable(final String headerURL, final String color) {
|
||||
mHeaderURL = headerURL;
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
private void loadFromPrefs() {
|
||||
SharedPreferences prefs = GeckoSharedPrefs.forProfile(mApplication);
|
||||
mSavedURL = prefs.getString(PREFS_URL, null);
|
||||
mSavedColor = prefs.getString(PREFS_COLOR, null);
|
||||
}
|
||||
|
||||
private void saveToPrefs() {
|
||||
GeckoSharedPrefs.forProfile(mApplication)
|
||||
.edit()
|
||||
.putString(PREFS_URL, mHeaderURL)
|
||||
.putString(PREFS_COLOR, mColor)
|
||||
.apply();
|
||||
|
||||
// Let's keep the saved data in sync.
|
||||
mSavedURL = mHeaderURL;
|
||||
mSavedColor = mColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Load the data from preferences, if it exists.
|
||||
loadFromPrefs();
|
||||
|
||||
if (TextUtils.isEmpty(mHeaderURL)) {
|
||||
// mHeaderURL is null is this is the early startup path. Use
|
||||
// the saved values, if we have any.
|
||||
mHeaderURL = mSavedURL;
|
||||
mColor = mSavedColor;
|
||||
if (TextUtils.isEmpty(mHeaderURL)) {
|
||||
// We don't have any saved values, so we probably don't have
|
||||
// any lightweight theme set yet.
|
||||
return;
|
||||
}
|
||||
} else if (TextUtils.equals(mHeaderURL, mSavedURL)) {
|
||||
// If we are already using the given header, just return
|
||||
// without doing any work.
|
||||
return;
|
||||
} else {
|
||||
// mHeaderURL and mColor probably need to be saved if we get here.
|
||||
saveToPrefs();
|
||||
}
|
||||
|
||||
String croppedURL = mHeaderURL;
|
||||
int mark = croppedURL.indexOf('?');
|
||||
if (mark != -1) {
|
||||
croppedURL = croppedURL.substring(0, mark);
|
||||
}
|
||||
|
||||
if (croppedURL.startsWith(ASSETS_PREFIX)) {
|
||||
onBitmapLoaded(loadFromAssets(croppedURL));
|
||||
} else {
|
||||
onBitmapLoaded(BitmapUtils.decodeUrl(croppedURL));
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap loadFromAssets(String url) {
|
||||
InputStream stream = null;
|
||||
|
||||
try {
|
||||
stream = mApplication.getAssets().open(url.substring(ASSETS_PREFIX.length()));
|
||||
return BitmapFactory.decodeStream(stream);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onBitmapLoaded(final Bitmap bitmap) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setLightweightTheme(bitmap, mColor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public LightweightTheme(Application application) {
|
||||
mApplication = application;
|
||||
mListeners = new ArrayList<OnChangeListener>();
|
||||
|
||||
// unregister isn't needed as the lifetime is same as the application.
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this,
|
||||
"LightweightTheme:Update",
|
||||
"LightweightTheme:Disable");
|
||||
|
||||
ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable());
|
||||
}
|
||||
|
||||
public void addListener(final OnChangeListener listener) {
|
||||
// Don't inform the listeners that attached late.
|
||||
// Their onLayout() will take care of them before their onDraw();
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(OnChangeListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
try {
|
||||
if (event.equals("LightweightTheme:Update")) {
|
||||
JSONObject lightweightTheme = message.getJSONObject("data");
|
||||
final String headerURL = lightweightTheme.getString("headerURL");
|
||||
final String color = lightweightTheme.optString("accentcolor");
|
||||
|
||||
ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color));
|
||||
} else if (event.equals("LightweightTheme:Disable")) {
|
||||
// Clear the saved data when a theme is disabled.
|
||||
// Called on the Gecko thread, but should be very lightweight.
|
||||
clearPrefs();
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
resetLightweightTheme();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the data stored in preferences for fast path loading during startup
|
||||
*/
|
||||
private void clearPrefs() {
|
||||
GeckoSharedPrefs.forProfile(mApplication)
|
||||
.edit()
|
||||
.remove(PREFS_URL)
|
||||
.remove(PREFS_COLOR)
|
||||
.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new lightweight theme with the given bitmap.
|
||||
* Note: This should be called on the UI thread to restrict accessing the
|
||||
* bitmap to a single thread.
|
||||
*
|
||||
* @param bitmap The bitmap used for the lightweight theme.
|
||||
* @param color The background/accent color used for the lightweight theme.
|
||||
*/
|
||||
private void setLightweightTheme(Bitmap bitmap, String color) {
|
||||
if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
|
||||
mBitmap = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the max display dimension so we can crop or expand the theme.
|
||||
final int maxWidth = WindowUtils.getLargestDimension(mApplication);
|
||||
|
||||
// The lightweight theme image's width and height.
|
||||
final int bitmapWidth = bitmap.getWidth();
|
||||
final int bitmapHeight = bitmap.getHeight();
|
||||
|
||||
try {
|
||||
mColor = Color.parseColor(color);
|
||||
} catch (Exception e) {
|
||||
// Malformed or missing color.
|
||||
// Default to TRANSPARENT.
|
||||
mColor = Color.TRANSPARENT;
|
||||
}
|
||||
|
||||
// Calculate the luminance to determine if it's a light or a dark theme.
|
||||
double luminance = (0.2125 * ((mColor & 0x00FF0000) >> 16)) +
|
||||
(0.7154 * ((mColor & 0x0000FF00) >> 8)) +
|
||||
(0.0721 * (mColor & 0x000000FF));
|
||||
mIsLight = luminance > 110;
|
||||
|
||||
// The bitmap image might be smaller than the device's width.
|
||||
// If it's smaller, fill the extra space on the left with the dominant color.
|
||||
if (bitmapWidth >= maxWidth) {
|
||||
mBitmap = Bitmap.createBitmap(bitmap, bitmapWidth - maxWidth, 0, maxWidth, bitmapHeight);
|
||||
} else {
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
|
||||
// Create a bigger image that can fill the device width.
|
||||
// By creating a canvas for the bitmap, anything drawn on the canvas
|
||||
// will be drawn on the bitmap.
|
||||
mBitmap = Bitmap.createBitmap(maxWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(mBitmap);
|
||||
|
||||
// Fill the canvas with dominant color.
|
||||
canvas.drawColor(mColor);
|
||||
|
||||
// The image should be top-right aligned.
|
||||
Rect rect = new Rect();
|
||||
Gravity.apply(Gravity.TOP | Gravity.RIGHT,
|
||||
bitmapWidth,
|
||||
bitmapHeight,
|
||||
new Rect(0, 0, maxWidth, bitmapHeight),
|
||||
rect);
|
||||
|
||||
// Draw the bitmap.
|
||||
canvas.drawBitmap(bitmap, null, rect, paint);
|
||||
}
|
||||
|
||||
for (OnChangeListener listener : mListeners) {
|
||||
listener.onLightweightThemeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the lightweight theme.
|
||||
* Note: This should be called on the UI thread to restrict accessing the
|
||||
* bitmap to a single thread.
|
||||
*/
|
||||
private void resetLightweightTheme() {
|
||||
ThreadUtils.assertOnUiThread(AssertBehavior.NONE);
|
||||
if (mBitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the bitmap.
|
||||
mBitmap = null;
|
||||
|
||||
for (OnChangeListener listener : mListeners) {
|
||||
listener.onLightweightThemeReset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A lightweight theme is enabled only if there is an active bitmap.
|
||||
*
|
||||
* @return True if the theme is enabled.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return (mBitmap != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the luminance of the domanint color, a theme is classified as light or dark.
|
||||
*
|
||||
* @return True if the theme is light.
|
||||
*/
|
||||
public boolean isLightTheme() {
|
||||
return mIsLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop the image based on the position of the view on the window.
|
||||
* Either the View or one of its ancestors might have scrolled or translated.
|
||||
* This value should be taken into account while mapping the View to the Bitmap.
|
||||
*
|
||||
* @param view The view requesting a cropped bitmap.
|
||||
*/
|
||||
private Bitmap getCroppedBitmap(View view) {
|
||||
if (mBitmap == null || view == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the global position of the view on the entire screen.
|
||||
Rect rect = new Rect();
|
||||
view.getGlobalVisibleRect(rect);
|
||||
|
||||
// Get the activity's window position. This does an IPC call, may be expensive.
|
||||
Rect window = new Rect();
|
||||
view.getWindowVisibleDisplayFrame(window);
|
||||
|
||||
// Calculate the coordinates for the cropped bitmap.
|
||||
int screenWidth = view.getContext().getResources().getDisplayMetrics().widthPixels;
|
||||
int left = mBitmap.getWidth() - screenWidth + rect.left;
|
||||
int right = mBitmap.getWidth() - screenWidth + rect.right;
|
||||
int top = rect.top - window.top;
|
||||
int bottom = rect.bottom - window.top;
|
||||
|
||||
int offsetX = 0;
|
||||
int offsetY = 0;
|
||||
|
||||
// Find if this view or any of its ancestors has been translated or scrolled.
|
||||
ViewParent parent;
|
||||
View curView = view;
|
||||
do {
|
||||
offsetX += (int) curView.getTranslationX() - curView.getScrollX();
|
||||
offsetY += (int) curView.getTranslationY() - curView.getScrollY();
|
||||
|
||||
parent = curView.getParent();
|
||||
|
||||
if (parent instanceof View) {
|
||||
curView = (View) parent;
|
||||
}
|
||||
|
||||
} while (parent instanceof View);
|
||||
|
||||
// Adjust the coordinates for the offset.
|
||||
left -= offsetX;
|
||||
right -= offsetX;
|
||||
top -= offsetY;
|
||||
bottom -= offsetY;
|
||||
|
||||
// The either the required height may be less than the available image height or more than it.
|
||||
// If the height required is more, crop only the available portion on the image.
|
||||
int width = right - left;
|
||||
int height = (bottom > mBitmap.getHeight() ? mBitmap.getHeight() - top : bottom - top);
|
||||
|
||||
// There is a chance that the view is not visible or doesn't fall within the phone's size.
|
||||
// In this case, 'rect' will have all values as '0'. Hence 'top' and 'bottom' may be negative,
|
||||
// and createBitmap() will fail.
|
||||
// The view will get a background in its next layout pass.
|
||||
try {
|
||||
return Bitmap.createBitmap(mBitmap, left, top, width, height);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cropped bitmap to a BitmapDrawable and returns the same.
|
||||
*
|
||||
* @param view The view for which a background drawable is required.
|
||||
* @return Either the cropped bitmap as a Drawable or null.
|
||||
*/
|
||||
public Drawable getDrawable(View view) {
|
||||
Bitmap bitmap = getCroppedBitmap(view);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BitmapDrawable drawable = new BitmapDrawable(view.getContext().getResources(), bitmap);
|
||||
drawable.setGravity(Gravity.TOP | Gravity.RIGHT);
|
||||
drawable.setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cropped bitmap to a LightweightThemeDrawable, placing it over the dominant color.
|
||||
*
|
||||
* @param view The view for which a background drawable is required.
|
||||
* @return Either the cropped bitmap as a Drawable or null.
|
||||
*/
|
||||
public LightweightThemeDrawable getColorDrawable(View view) {
|
||||
return getColorDrawable(view, mColor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cropped bitmap to a LightweightThemeDrawable, placing it over the required color.
|
||||
*
|
||||
* @param view The view for which a background drawable is required.
|
||||
* @param color The color over which the drawable should be drawn.
|
||||
* @return Either the cropped bitmap as a Drawable or null.
|
||||
*/
|
||||
public LightweightThemeDrawable getColorDrawable(View view, int color) {
|
||||
return getColorDrawable(view, color, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cropped bitmap to a LightweightThemeDrawable, placing it over the required color.
|
||||
*
|
||||
* @param view The view for which a background drawable is required.
|
||||
* @param color The color over which the drawable should be drawn.
|
||||
* @param needsDominantColor A layer of dominant color is needed or not.
|
||||
* @return Either the cropped bitmap as a Drawable or null.
|
||||
*/
|
||||
public LightweightThemeDrawable getColorDrawable(View view, int color, boolean needsDominantColor) {
|
||||
Bitmap bitmap = getCroppedBitmap(view);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LightweightThemeDrawable drawable = new LightweightThemeDrawable(view.getContext().getResources(), bitmap);
|
||||
if (needsDominantColor) {
|
||||
drawable.setColorWithFilter(color, (mColor & 0x22FFFFFF));
|
||||
} else {
|
||||
drawable.setColor(color);
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.lwt;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.ComposeShader;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* A special drawable used with lightweight themes. This draws a color
|
||||
* (with an optional color-filter) and a bitmap (with a linear gradient
|
||||
* to specify the alpha) in order.
|
||||
*/
|
||||
public class LightweightThemeDrawable extends Drawable {
|
||||
private final Paint mPaint;
|
||||
private Paint mColorPaint;
|
||||
|
||||
private final Bitmap mBitmap;
|
||||
private final Resources mResources;
|
||||
|
||||
private int mStartColor;
|
||||
private int mEndColor;
|
||||
|
||||
public LightweightThemeDrawable(Resources resources, Bitmap bitmap) {
|
||||
mBitmap = bitmap;
|
||||
mResources = resources;
|
||||
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeWidth(0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
super.onBoundsChange(bounds);
|
||||
initializeBitmapShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
// Draw the colors, if available.
|
||||
if (mColorPaint != null) {
|
||||
canvas.drawPaint(mColorPaint);
|
||||
}
|
||||
|
||||
// Draw the bitmap.
|
||||
canvas.drawPaint(mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
// A StateListDrawable will reset the alpha value with 255.
|
||||
// We cannot use to be the bitmap alpha.
|
||||
mPaint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter filter) {
|
||||
mPaint.setColorFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a paint that paint a particular color.
|
||||
*
|
||||
* Note that the given color should include an alpha value.
|
||||
*
|
||||
* @param color The color to be painted.
|
||||
*/
|
||||
public void setColor(int color) {
|
||||
mColorPaint = new Paint(mPaint);
|
||||
mColorPaint.setColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a paint that paint a particular color, and a filter for the color.
|
||||
*
|
||||
* Note that the given color should include an alpha value.
|
||||
*
|
||||
* @param color The color to be painted.
|
||||
* @param filter The filter color to be applied using SRC_OVER mode.
|
||||
*/
|
||||
public void setColorWithFilter(int color, int filter) {
|
||||
mColorPaint = new Paint(mPaint);
|
||||
mColorPaint.setColor(color);
|
||||
mColorPaint.setColorFilter(new PorterDuffColorFilter(filter, PorterDuff.Mode.SRC_OVER));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the alpha for the linear gradient used with the bitmap's shader.
|
||||
*
|
||||
* @param startAlpha The starting alpha (0..255) value to be applied to the LinearGradient.
|
||||
* @param startAlpha The ending alpha (0..255) value to be applied to the LinearGradient.
|
||||
*/
|
||||
public void setAlpha(int startAlpha, int endAlpha) {
|
||||
mStartColor = startAlpha << 24;
|
||||
mEndColor = endAlpha << 24;
|
||||
initializeBitmapShader();
|
||||
}
|
||||
|
||||
private void initializeBitmapShader() {
|
||||
// A bitmap-shader to draw the bitmap.
|
||||
// Clamp mode will repeat the last row of pixels.
|
||||
// Hence its better to have an endAlpha of 0 for the linear-gradient.
|
||||
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
|
||||
// A linear-gradient to specify the opacity of the bitmap.
|
||||
LinearGradient gradient = new LinearGradient(0, 0, 0, mBitmap.getHeight(), mStartColor, mEndColor, Shader.TileMode.CLAMP);
|
||||
|
||||
// Make a combined shader -- a performance win.
|
||||
// The linear-gradient is the 'SRC' and the bitmap-shader is the 'DST'.
|
||||
// Drawing the DST in the SRC will provide the opacity.
|
||||
mPaint.setShader(new ComposeShader(bitmapShader, gradient, PorterDuff.Mode.DST_IN));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue