Blur on Android - android

I am creating a blurred background for an Android app and having trouble with different versions.
While it seems I solved it for all cases, I don't actually have exact conditions on how to decide which case it is.
Right now, it seems this is correct:
/*
Here lies the question. Is this condition good enough? If android 10 or hw acceleration is off
*/
if (android.sdk_int >= 29 || hwAcceleration == false) {
// draw circle with blurred paint
Paint().asFrameworkPaint().apply {
maskFilter = BlurMaskFilter(radius, BlurMaskFilter.Blur.NORMAL)
}
} else {
// load drawable into bitmap and use deprecated RenderScript to blur it
RenderScript.create(this)
ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
...
}
According to this table, MaskFilter is not supported at all when hw accelerated, but that is not my experience with android emulators of version 10+. Is the table just not updated?

Related

How to detect emoji support on Android by code

By code, I can make a button that inserts these 3 emojis into the text: ⚽️😈🐺
On many phones when the user clicks the button, though, the problem is that ⚽️😈🐺 displays as [X][X][X]. Or even worse, it displays only three empty spaces.
I would like to disable and hide my own built-in emoji-keypad on Android devices that do not display emojis correctly. Does anyone knows or have a tip on how to detect in code if a device has emoji support?
I have read that emoji is supported from android 4.1, but that is not my experience....
I just implemented a solution for this problem myself. The nice thing with Android is that it is open source so that when you come around problems like these, there's a good chance you can find an approach to help you.
In the Android Open Source Project, you can find a method where they use Paint.hasGlyph to detect whether a font exists for a given emoji. However, as this method is not available before API 23, they also do test renders and compare the result against the width of 'tofu' (the [x] character you mention in your post.)
There are some other failings with this approach, but it should be enough to get you started.
Google source:
https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/master/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java#441
https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/master/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
Based on Jason Gore answer:
For example create boolean canShowFlagEmoji:
private static boolean canShowFlagEmoji() {
Paint paint = new Paint();
String switzerland = "\uD83C\uDDE8\uD83C\uDDED"; // Here enter Surrogates of Emoji
try {
return paint.hasGlyph(switzerland);
} catch (NoSuchMethodError e) {
// Compare display width of single-codepoint emoji to width of flag emoji to determine
// whether flag is rendered as single glyph or two adjacent regional indicator symbols.
float flagWidth = paint.measureText(switzerland);
float standardWidth = paint.measureText("\uD83D\uDC27"); // U+1F427 Penguin
return flagWidth < standardWidth * 1.25;
// This assumes that a valid glyph for the flag emoji must be less than 1.25 times
// the width of the penguin.
}
}
And then in code whenever when you need to check if emoji is available:
if (canShowFlagEmoji()){
// Code when FlagEmoji is available
} else {
// And when not
}
Surrogates of emoji you can get here, when you click on detail.
An alternative option might be to include the Android "Emoji Compatibility" library, which would detect and add any required Emoji characters to apps running on Android 4.4 (API 19) and later: https://developer.android.com/topic/libraries/support-library/preview/emoji-compat.html
final Paint paint = new Paint();
final boolean isEmojiRendered;
if (VERSION.SDK_INT >= VERSION_CODES.M) {
isEmojiRendered = paint.hasGlyph(emoji);
}
else{
isEmojiRendered = paint.measureText(emoji) > 7;
}
The width > 7 part is particularly hacky, I would expect the value to be 0.0 for non-renderable emoji, but across a few devices, I found that the value actually ranged around 3.0 to 6.0 for non-renderable, and 12.0 to 15.0 for renderable. Your results may vary so you might want to test that. I believe the font size also has an effect on the output of measureText() so keep that in mind.
The second part was answerd by RogueBaneling here how can I check if my device is capable to render Emoji images correctly?

Trying to draw on canvas using canvas.clipPath

I am trying to draw circle on darken background, trying to achive smth like this -
This actually worked on my Samsung s4 and Samsung tab 3, but won't work on s2 and some emulator (all sorrounding viewgroup is darken, and inside oval too, seems like it does not see my circleSelectionPath). Help me please to find the way to make it work on every device
final Paint paint = new Paint();
paint.setColor(Color.parseColor("#77000000"));
Path circleSelectionPath = new Path();
mRectF.set(l, t, r, b);
circleSelectionPath.addOval(mRectF, Path.Direction.CW);
canvas.clipPath(circleSelectionPath, Region.Op.XOR);
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom, paint );
canvas.restore();
bitmapRect contains my viewgroup dimens (for example : 0,0, 500,500)
Got it. Android has a bug with canvas.clipRect, they have optimized it, but on some android apis it simple don't work after optimization ) I found an issue.
So fix - disable hrdware acceleration for this view
(Build.VERSION.SDK_INT <= 19 && mCropShape == CropImageView.CropShape.OVAL) {
//TURN off hardware acceleration
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

Path.offset() not working on every device

I have some problems with android.graphics.path.
I am developing a game. I have some Paths. They don't change there sizes, shapes. I draw them onto a canvas. I move them in every game's frame. So I set an offset: dx
It works fine on many devices:
Nokia X Dual Sim (Android 4.4.4 Cyanogenmode)(API level 19)
Samsung Galaxy Duos (GT-S7562)(Android 4.0.4)(API level 15)
Sony X8 (Android 2.3.7 Cyanogenmode)(API level 10)
and on some others
But it not works well on some other devices:
Samsung Galaxy Ace II (Android 4.1.2)(API level 16)
Nokia X Dual Sim (Nokia X platform 1.2)(Android Studio shows: API level 16)
and on some others
I set the offset in my draw method:
path.offset(dX, 0);
Not working means: it not moves. The system draws it to the original position.
But when it works, it works descent (moves, and it is fast)
I also tried with Matrixes:
translateMatrix = new Matrix();
translateMatrix.setTranslate(dX, 0);
path.transform(translateMatrix);
The same happened.
I see something on developer site of Android:
http://developer.android.com/reference/android/graphics/Path.html
void offset(float dx, float dy)
Offset the path by (dx,dy), returning true on success.
??? It must have changed, that's why here is an error
It may in connection with the Hardware Accelerated mode:
https://groups.google.com/forum/#!topic/android-developers/HgGVSbSghpk
I support from Api level 9, but I turned Harware Accelerated mode. (It works form API level 14)
The problem also shows up when I turn it off.
I don't see why is it sometimes work over API level 14, and sometimes why not?
It says, hardware accelerated mode supports Path:
http://android-developers.blogspot.de/2011/03/android-30-hardware-acceleration.html
Do you know what is the problem with this? What should I do?
Something that mixes it up more:
This works on every device (but works slower - it lags):
Canvas temp = new Canvas(bitmap);
path.draw(temp);
originalCanvas.drawBitmap(bitmap, 0, 0, myPaint);
So
I make a temporary canvas
I set it on a bitmap
I draw on that canvas (it will be on the bitmap)
I draw the bitmap onto the original canvas
Why does it work here?
Please help me how should I make it work.
I made it work, but not with the offset
I made my own offset function:
it saves the last position, and calculates the new one with the
offset.
Then it creates a new Path without using operator new
calculateNewPoints();
wallpath.rewind();
wallpath.moveTo(newPosX, newPosY);
.
.
.
wallpath.close();
It performs well :)
On API level 16 on manifest add the following attribute to the application
<application android:hardwareAccelerated="false" ...>
Indeed there seems to be a bug related to Path.offset() on older Android devices.
I have purchased an 8 years old "Samsung Galaxy S3" phone with Android 4.1.2 to test my word game and was surprised to discover, that Paths in the app are not filled:
My workaround is shown below, for older Android devices I do not use Path.offset() anymore, but instead Canvas.translate() and then draw the not offsetted Path object:
public class LetterTile extends RectF {
// consider anything older then Android 6.0 Marshmallow as an old device
public static final boolean TOO_OLD =
(Build.VERSION.SDK_INT < Build.VERSION_CODES.M);
private final Path mPathFill = new Path();
// mPathFillMoved is same as mPathFill, but offset by left, top
// it is used on newer devices to paint gradient over all tiles
private final Path mPathFillMoved = new Path();
public void draw(Canvas canvas, Paint gradientPaint) {
if (!TOO_OLD) {
// the mPathFillMoved only works on newer Android devices
canvas.drawPath(mPathFillMoved, gradientPaint);
}
canvas.save();
canvas.translate(left, top);
if (TOO_OLD) {
// on older Android devices just draw mPathFilled in plain yellow
canvas.drawPath(mPathFill, gradientPaint);
}
canvas.restore();

how can I check if my device is capable to render Emoji images correctly?

I'm using Emoji unicode in a View. On most devices the images appear ok, but on one of my low-end device(android 2.3) they are rendered as little squares.
Can I check whether the device support emoji? So that I can publish my apk while won't show that ugly squares on some devices.
This is a late answer but I recently ran into a similar problem. I needed to filter through a List<String> and filter out emojis that couldn't be rendered on the device (i.e., if the device was old and didn't support rendering them).
What I ended up doing was using Paint to measure the text width.
Paint mPaint = new Paint();
private boolean emojiRenderable(String emoji) {
float width = mPaint.measureText(emoji);
if (width > 7) return true;
return false;
}
The width > 7 part is particularly hacky, I would expect the value to be 0.0 for non-renderable emoji, but across a few devices, I found that the value actually ranged around 3.0 to 6.0 for non-renderable, and 12.0 to 15.0 for renderable. Your results may vary so you might want to test that. I believe the font size also has an effect on the output of measureText() so keep that in mind.
Overall I am not sure if this is a great solution but it's the best that I've come up with so far.
please check the source code from Googles Mozc project.
The EmojiRenderableChecker class seems to work pretty well!
https://github.com/google/mozc/blob/master/src/android/src/com/google/android/inputmethod/japanese/emoji/EmojiRenderableChecker.java
it's like a compat version for Paint.hasGlypgh (added in Marshmallow).
https://developer.android.com/reference/android/graphics/Paint.html#hasGlyph(java.lang.String)
https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/master/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java#441
Inspired from the two methods found in the above file.
public static boolean canShowEmoji(String emoji) {
Paint paint = new Paint();
float tofuWidth = paint.measureText("\uFFFE");
float standardWidth = paint.measureText("\uD83D\uDC27");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return paint.hasGlyph(emoji);
} else {
float emojiWidth = paint.measureText(emoji);
return emojiWidth > tofuWidth && emojiWidth < standardWidth * 1.25;
// This assumes that a valid glyph for the cheese wedge must be greater than the width
// of the noncharacter.
}
}

Android Paint PorterDuff.Mode.CLEAR not working properly

I'm trying to create an application similar to Finger Paint example from Android SDK. I was trying to implement Undo/Redo functionality to my test app and used the accepted answer in this question : Android FingerPaint Undo/Redo implementation.
The example there is working, but there is a strange things which I've noticed. If I select eraser mode for example on some button click, the default implementation acts like a eraser, but using onDraw() as in the question mentioned above is not doing this. Instead of it's acting like a normal brush and drawing with a black stroke (depending on the given color).
If I try to add another effects to the current brush, for example I drew 15 lines and after that select add blur option, after I draw the new one, all lines before get blurred too.
if (mPaint.getMaskFilter() != mBlur) {
mPaint.setMaskFilter(mBlur);
} else {
mPaint.setMaskFilter(null);
}
return true;
So my question is..any ideas how can I separate old lines from new ones and setting effects only for them and using clear mode as it should be?
Thanks for any kind of help!
You would rather hold history in an object like the following one than just holding the path objects..
class pathInfo {
Path mPath;
int mStyle;
boolean mbAntiAlias;
....
}
Then while drawing each path pick each corresponding info from these objects
protected void onDraw(Canvas canvas) {
for (PathInfo p : pathsInfo){
mPaint.setStyle(p.mStyle);
....
canvas.drawPath(p.mPath, mPaint);
}
}

Categories

Resources