Snackbar and other animations stopped working on some Android devices - android

I have a very odd issue that I cannot figure out. I was not an issue until recently but I can't seem to revert back to prevent it. Also the other odd thing is it works on some devices and others it does not.
The issue is animations. One in particular is snack bar. The snackbar should animate up and down but it is not. it just shows then hides. check video below to see issue.
Video of issue
Here is the Android code to animate the snackbar in
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewCompat.setTranslationY(mView, mView.getHeight());
ViewCompat.animate(mView)
.translationY(0f)
.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR)
.setDuration(ANIMATION_DURATION)
.setListener(new ViewPropertyAnimatorListenerAdapter() {
#Override
public void onAnimationStart(View view) {
mView.animateChildrenIn(ANIMATION_DURATION - ANIMATION_FADE_DURATION,
ANIMATION_FADE_DURATION);
}
#Override
public void onAnimationEnd(View view) {
onViewShown();
}
}).start();
}
Its using ViewCompat for the v4 Library. I have other animations working in another activity working fine. Also the issue is not on just one activity its all of them. That makes me think its system wide somehow. But they all use different internal themes but all extend Theme.AppCompat.NoActionBar.
Here is my main layout
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="4dp">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:titleTextAppearance="#style/ToolbarTitle"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways|snap"/>
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextAppearance="#style/TabText"
app:tabMinWidth="#dimen/tab_minwidth"
app:tabMode="fixed"
app:tabGravity="fill"
app:layout_scrollFlags="enterAlways"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" >
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<fr.castorflex.android.circularprogressbar.CircularProgressBar
android:id="#+id/base_progressSpinner"
android:layout_gravity="center"
android:layout_width="48dp"
android:layout_height="48dp"
android:indeterminate="true"
android:visibility="invisible"
app:cpb_color="#color/spinner"
app:cpb_rotation_speed="1.0"
app:cpb_sweep_speed="1.0"
app:cpb_stroke_width="4dp"
app:cpb_min_sweep_angle="10"
app:cpb_max_sweep_angle="300"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab_upload"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:layout_anchor="#id/content_frame"
app:layout_anchorGravity="bottom|right|end"
app:borderWidth="0dp"
android:src="#drawable/app_fab_upload"
android:layout_margin="#dimen/big_padding"
android:clickable="true"
app:backgroundTint="#color/fab_social"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab_muzei"
android:visibility="gone"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
app:layout_anchor="#id/content_frame"
app:layout_anchorGravity="bottom|right|end"
app:borderWidth="0dp"
android:src="#drawable/app_fab_muzei"
android:layout_margin="#dimen/big_padding"
android:clickable="true"
app:backgroundTint="#color/fab_social"/>
</android.support.design.widget.CoordinatorLayout>
Devices it works on
Nexus 9 (Marshmallow)
Nexus 4 (KitKat)
Galaxy S7 (Marshmallow)
Devices it does not work
Droid Turbo 2 (Marshmallow)
Galaxy S7 (Marshmallow) *my device works, my testers does not
Nexus 6p (Android N)
The other animation issues are with Switches. I have 2 in same layout and one stutters when switched and the other just switches with no animation.
I also have a LayoutTransition set to my AppBarLayout to animation the hiding/showing of my TabLayout and it works fine and all devices

I found the reason why this is happening, but not how to fix yet.
/**
* Returns true if we should animate the Snackbar view in/out.
*/
private boolean shouldAnimate() {
return !mAccessibilityManager.isEnabled();
}
That is called by Snackbar class and is false on working devices, and true on devices not working. Does anyone know about this?
So after i disabled lastpass in my system settings, accessibility the snackbar now animates as it should. That is crazy how that works. Nova launcher has the same affect. I guess any service in accessibility that is enabled will cause the snackbar animation to not work.

As Bignadad mentioned, the problem is that any accessibility feature, including things like password managers, disables the snackbar animations. Google, as of this edit, has fixed this for AndroidX but not the Design support library
Because Snackbar's base class, BaseTransientBottomBar, handles the animation, with package private, final methods, you have two choices if you want to fix it: roll your own snackbar from scratch, or use a more hacky solution with reflection:
Kotlin example:
// Only force when necessary, and don't animate when TalkBack or similar services are enabled
val shouldForceAnimate = !accessibilityManager.isEnabled && accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty()
Snackbar.make(coordinatorLayout, text, duration).apply {
if (shouldForceAnimate) {
try {
val accManagerField = BaseTransientBottomBar::class.java.getDeclaredField("mAccessibilityManager")
accManagerField.isAccessible = true
val accManager = accManagerField.get(this)
AccessibilityManager::class.java.getDeclaredField("mIsEnabled").apply {
isAccessible = true
setBoolean(accManager, false)
}
accManagerField.set(this, accManager)
} catch (e: Exception) {
Log.d("Snackbar", "Reflection error: $e")
}
}
}.show()
Java example:
// Only force when necessary, and don't animate when TalkBack or similar services are enabled
boolean shouldForceAnimate = !accessibilityManager.isEnabled() && accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN).size == 0;
Snackbar snackbar = Snackbar.make(coordinatorLayout, text, duration);
if(shouldForceAnimate){
try {
Field accManagerField = BaseTransientBottomBar.class.getDeclaredField("mAccessibilityManager");
accManagerField.setAccessible(true);
AccessibilityManager accManager = (AccessibilityManager) accManagerField.get(snackbar);
Field isEnabledField = AccessibilityManager.class.getDeclaredField("mIsEnabled");
isEnabledField.setAccessible(true);
isEnabledField.setBoolean(accManager, false);
accManagerField.set(snackbar, accManager);
} catch (Exception e) {
Log.d("Snackbar", "Reflection error: " + e.toString());
}
}
snackbar.show();
I'd love a third option here, but I'm not aware of one, at least until AndroidX gets out of beta with the proper fix.

This has been fixed since Material Components for Android 1.0.0-alpha3 with this commit.
Use it instead of the Design Library (which is the way to go if you're using AndroidX):
implementation "com.google.android.material:material:$material_components_version"

Related

Android - Detecting when a view's constraints have been fully resolved/loaded

I have quite a complex problem to do with Android views. I am creating a paint application, and I have two views: a transparent background view and the pixel art board.
For both views, I want the height and width to be calculated off of the distance between view A and B:
Instead of calculating the distance between these two views, I simply 'constraint' a view in the middle like so, and then extract its height by using its measuredHeight property (and yes, you could also calculate the distance between view A and B in the code, but my problem still remains when I try that):
Now, here's the XML code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/fragment_background_color_daynight"
tools:context=".activities.canvas.CanvasActivity">
<View
android:id="#+id/activityCanvas_topView"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#color/fragment_background_color_daynight"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.therealbluepandabear.pixapencil.customviews.colorswitcherview.ColorSwitcherView
android:id="#+id/activityCanvas_colorSwitcherView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:isPrimarySelected="false"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_colorPickerRecyclerView"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_topView"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_colorPickerRecyclerView"
app:primaryColor="#android:color/holo_green_dark"
app:secondaryColor="#color/black" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/activityCanvas_colorPickerRecyclerView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_topView"
app:layout_constraintEnd_toStartOf="#+id/activityCanvas_colorSwitcherView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_primaryFragmentHost"
tools:listitem="#layout/color_picker_layout" />
<FrameLayout
android:id="#+id/activityCanvas_distanceContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView" />
<com.google.android.material.card.MaterialCardView
android:id="#+id/fragmentOuterCanvas_canvasFragmentHostCardViewParent"
style="#style/activityCanvas_canvasFragmentHostCardViewParent_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="1dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView">
<!-- At runtime, the width and height will be calculated -->
<com.therealbluepandabear.pixapencil.customviews.transparentbackgroundview.TransparentBackgroundView
android:id="#+id/activityCanvas_transparentBackgroundView"
android:layout_width="0dp"
android:layout_height="0dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.tabs.TabLayout
android:id="#+id/activityCanvas_tabLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:tabStripEnabled="false"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_viewPager2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_tools_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_filters_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_color_palettes_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_brushes_str" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/activityCanvas_viewPager2"
android:layout_width="0dp"
android:layout_height="110dp"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/activityCanvas_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<FrameLayout
android:id="#+id/activityCanvas_primaryFragmentHost"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Of course, when it comes to calculating, I thought it would be best practise to utilize AndroidX's OneShotPreDrawListener, like so:
OneShotPreDrawListener.add(binding.root) {
binding.activityCanvasTransparentBackgroundView!!.setViewWidth(binding.activityCanvasDistanceContainer!!.measuredHeight)
binding.activityCanvasTransparentBackgroundView!!.setViewHeight(binding.activityCanvasDistanceContainer!!.measuredHeight)
}
Now, for some reason, the result looks like so:
Why is this the case!
I did some debugging, and when I log the height of view C, I get the following:
This is wrong. So, as an experiment, I added a GlobalLayoutListener to detect when exactly the view's constraints get resolved:
binding.activityCanvasDistanceContainer?.viewTreeObserver?.addOnGlobalLayoutListener( object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d("M_LOG", binding.activityCanvasDistanceContainer?.measuredHeight.toString())
}
})
Result:
So, after the first couple of times it's 438, until after the 4th/5th time it shoots up to 1000.
I am really confused why this is happening, as I want to run the event when the constraints have been fully resolved and calculated, but using OneShotPreDrawListener (or any other alternative) is just running the event when the view has been drawn, but not yet when it has been positioned properly.
I am confused what to do. How can I run an event when the view's constraints have been fully calculated?
Edit for Chetichamp:
I have debugged this and I think I can reproduce this error and tell you in what scenario it occurs and in which scenario it does not.
Basically in my app, prior to activity creation, I have a fragment called NewProjectFragment, which looks like so:
Here's the code for when the 'Done' button is pressed:
binding.fragmentNewCanvasDoneButton.setOnClickListener {
checkForTitleError()
checkForWidthError()
checkForHeightError()
if (!invalidTitle && !invalidWidth && !invalidHeight) {
try {
val title =
binding.fragmentNewCanvasProjectTitleTextInputEditText.text.toString()
val widthValue: Int =
binding.fragmentNewCanvasWidthTextInputEditText.text.toString().toInt()
val heightValue: Int =
binding.fragmentNewCanvasHeightTextInputEditText.text.toString().toInt()
if (widthValue + heightValue >= 2000 && (requireActivity() as MainActivity).showLargeCanvasSizeWarning) {
val frameLayout: FrameLayout =
this#setOnClickListeners.activity?.layoutInflater?.inflate(
R.layout.dont_show_large_canvas_warning_again_checkbox,
requireView().findViewById(android.R.id.content),
false
)
as FrameLayout
val checkBox = frameLayout.getChildAt(0) as MaterialCheckBox
requireActivity().showDialog(
getString(R.string.generic_warning_in_code_str),
getString(R.string.dialog_large_canvas_warning_text_in_code_str),
getString(R.string.dialog_large_canvas_warning_positive_button_text_in_code_str),
{ _, _ ->
if (checkBox.isChecked) {
(requireActivity() as MainActivity).showLargeCanvasSizeWarning =
false
with((requireActivity() as MainActivity).sharedPreferenceObject.edit()) {
putBoolean(
StringConstants.Identifiers.SHARED_PREFERENCE_SHOW_LARGE_CANVAS_SIZE_WARNING_IDENTIFIER,
(requireActivity() as MainActivity).showLargeCanvasSizeWarning
)
apply()
}
}
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
},
getString(R.string.dialog_unsaved_changes_negative_button_text_in_code_str),
{ _, _ ->
},
frameLayout
)
} else {
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
}
} catch (exception: Exception) {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
} else {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
}
As you can see, it has a listener, so the code for the listener, which is in MainActivity, is like so (maybe this is causing the issue? and I just don't need a listener like this? I don't know if you think this is why I get the issue):
fun MainActivity.extendedOnDoneButtonPressed(projectTitle: String, width: Int, height: Int, spotLightInProgress: Boolean) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, projectTitle)
.putExtra(StringConstants.Extras.WIDTH_EXTRA, width)
.putExtra(StringConstants.Extras.HEIGHT_EXTRA, height)
.putExtra(StringConstants.Extras.SPOTLIGHT_IN_PROGRESS_EXTRA, spotLightInProgress)
)
}
Now, what I come to the conclusion is that all of this extra work is causing a delay, because when you simply tap on a pre-existing project, we can see that the intent is much simpler:
fun MainActivity.extendedOnCreationTapped(param: PixelArt) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.INDEX_EXTRA, pixelArtData.indexOf(param))
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, param.title))
}
With a simple intent like so, the problem doesn't get reproduced, and it sizes properly.
What I relaized is that the work done in NewProject fragment is causing a delay, and when I simply scrap out the work and perfom a simple intent, the problem is 'fixed'. I don't know how to fix this but hopefully it can help with finding a solution.
Debugging even further
When I debug the issue even further, I notice something strange. The measuredHeight of the root layout jumps up by one thousand:
This is not observed when the creation is tapped with the simple intent. I am lost for words as to how strange this bug is, I've never seen this in my life.
There is something going on with your code that is touching your views that is causing the multiple layouts. Without that code being posted, it is impossible to say what.
To help you with debugging, set the one shot to the following:
OneShotPreDrawListener.add(binding.root) {
Log.d("Applog", "OneShot")
binding.activityCanvasTransparentBackgroundView.layoutParams.width =
binding.activityCanvasDistanceContainer.measuredWidth
binding.activityCanvasTransparentBackgroundView.layoutParams.height =
binding.activityCanvasDistanceContainer.measuredHeight
binding.activityCanvasTransparentBackgroundView.requestLayout()
}
In my test, when placing this in the onCreateView() function of a fragment, "OneShot" is logged just once and the size of the view is adjusted as you say it should be. (btw, requestLayout() was required in this test.)
I would take a look at any other code that touches your views to see if something else is triggering the multiple calls: maybe setWidth() or setHeight().
I will add that I have noticed in the past that ConstraintLayout will call the global layout listener multiple times. Usually, I would say, the listener is removed on the first call, so this behavior would not be noticed.
Instead of the global layout listener or the predraw listener, you could try a layout listener:
binding.root.doOnLayout {
// Your code here
}
or
binding.root.doOnNextLayout {
// Your code here
}
doOnNextLayout is guaranteed to be called only once, while doOnLayout may be called multiple times.
Wow guys.
I finally found a solution, after 12 hours of nonstop debugging, almost going clinically insane, and logging and destroying my codebase.
I realized that the keyboard from the NewProjectFragment was cutting the view, causing the bug. Holy moly... I am ecstatic that I finally found why this is happening but at the same time shocked how I didn't discover this before!
The solution was to add the following to the activity:
<activity
android:name="com.therealbluepandabear.pixapencil.activities.canvas.CanvasActivity"
android:windowSoftInputMode="stateHidden" // this
android:hardwareAccelerated="true"
android:exported="false" />
Prior to this, the windowSoftInputMode was adjustResize -- causing the bug.

Android TextView.setText() is not changing on recent Samsung phones with SurfaceView Overlay

In my app I have a TextView and an ImageView that I update based on sensor information. This works perfectly on emulators and most of the handsets I have tried it on, however on Samsung handsets from the S7 to the S9 for some reason neither of them changes when they should be updated.
They are part of a Fragment overlaying a SurfaceView with a camera preview in it, there is a second SurfaceView overlay above them:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="myApp.CameraViewFragment">
<FrameLayout
android:id="#+id/control"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true">
<myApp.CameraSurfaceView
android:id="#+id/camera_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<TextView
android:id="#+id/direction"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:background="#color/black_overlay"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:textSize="30dp"
android:text="" />
<ImageButton
android:id="#+id/gpsNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/gps_unfixed"
android:layout_gravity="right"
android:tint="#color/gps_unfixed"
android:background="#null" />
</FrameLayout>
<com.myapp.OverlaySurfaceView
android:id="#+id/overlay_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true" />
</FrameLayout>
As far as I know, this is the relevant part of the Fragment code:
public class CameraViewFragment extends Fragment
{
private TextView directionView;
private CameraSurfaceView cameraView;
private OverlaySurfaceView overlay;
#Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
cameraView = (CameraSurfaceView) view.findViewById(R.id.camera);
directionView = (TextView) view.findViewById(R.id.direction);
overlay = (OverlaySurfaceview) view.findViewById(R.id.overlay_view);
}
private void setDirection(AccurateDirection direction) {
final String directionString = direction.getDirection();
overlay.update();
Activity activity = getActivity();
if ( activity != null ) {
activity.runOnUiThread(new Runnable() {
public void run() {
directionView.setText(directionString);
directionView.invalidate();
}
});
}
}
}
When I run this on a regular handset, the directionView text updates as new direction data arrives. On a Samsung S7 the value gets set once, maybe updated once right at the start and then it seems to stick and not change any more. If I pause in the debugger and call directionView.getText() from the immediate window, it gives me the result I am expecting, not what I see on the screen. If I call directionView.setText('ABC') in the immediate window, the value returned by directionView.getText() changes but again the value on the screen does not. The ImageView also seems to get stuck in its initial state regardless of changes that should adjust it.
Previous questions on this topic seem to involve updates not being triggered from the UI thread or failures to call invalidate on the view component, so I have fixed both of those already but the problem persists.
I have only seen this on recent Samsung handsets ( my old Samsung S4 appears to be fine ) which makes it hard to troubleshoot. Why are my view components not changing on the Samsung devices and what do I need to do to get them to update?
Edit: Apologies to anyone previously attempting to answer this as I had ommited the OverlaySurfaceView and I now realise that the other components update correctly if the OverlaySurfaceView is removed, so apparently having that second SurfaceView in the view hierarchy is interfering, but only on Samsung. Switching on and off hardware acceleration doesn't seem to make a difference, but there may be a specific place or way that I need to do it. On a working handset hardware acceleration is marked as disabled for the `OverlaySurfaceView.
I had the same issue with Samsung S7 device,
eventually I realized i was not handling the setZOrderOnTop correctly
check out this answer :
Button on top of SurfaceView with setZOrderOnTop set to true in Android

Android View.GONE not working as intended on rare occasions

I've been seeing an issue in an app I'm working on relating to views being visible when they shouldn't be and I was wondering if anyone had seen similar.
I have a fragment viewed from a viewPager, that fragment has a base layout, in that base layout I include a banner style area with some info in it and a button, the layout is defaulted to GONE in the XML, like this.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:animateLayoutChanges="true" >
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/usage_swipe_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:animateLayoutChanges="true"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/usage_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:background="#color/background_colour">
<include layout="#layout/layout_in_question"
android:id="#+id/layout_in_question_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
.... Normal Stuff in layout
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
This view is only set to VISIBLE in very select circumstances. However some users are seeing it outside of those circumstances based on a response from a server call, its a very low percentage of users who are seeing it when they shouldn't ( in the region of 0.001% ), the server team insist its not them so I'm trying to ascertain if there are any known android issue or "hacks" that allow this sort of thing. The App supports API 15 and up and we're currently using support libs 25.3.0.
Has anyone seen behavior like this before? are there developer options on some devices that "show all views" or Modded OS's that allow it?
Edit: While I cant share full code snippets due to NDA, I have 2 locations the view can be enabled, both call the same method.
private View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
setupBanner();
}
/**
* OTTO event based on server response
*/
#Subscribe
public void receiveEvent(BannerEvent event)
{
setupBanner();
}
public void setupBanner() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (shouldShowBanner()) {
findViewById(R.id.layout_in_question_id).setVisibility(View.VISIBLE);
}
}
});
}
shouldShowBanner returns a boolean which is either true/false depending on what the server responded with.

Android data binding on android 4.1 doesn't work

https://developer.android.com/topic/libraries/data-binding/index.html:
The Data Binding Library offers both flexibility and broad
compatibility — it's a support library, so you can use it with all
Android platform versions back to Android 2.1 (API level 7+).
But when I'm trying to use data binding in my project it just doesn't work, there's no reaction for that, TextViews are empty, onClick bindings just desn't workx, please help me I need support for android 4.1, google tells that it should to be possible even for 2.1, I don't think they're lying :/
Example code:
<FrameLayout
android:layout_width="130dp"
android:layout_height="0dp"
android:layout_weight="15"
android:layout_gravity="center"
android:background="#color/colorSecondary"
android:clickable="true"
android:onClick="#{ () -> view.save() }" <!-- binding -->
android:layout_margin="15dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="#string/save"
android:textAllCaps="true"
android:textColor="#color/colorForeground"/>
</FrameLayout>
Code behind:
public void save()
{
if(viewModel.car.save() != 0) //If you put breakpoint here it won't be hit
getLayoutRoot().goBack();
}
Code works fine on API level 19+
I found the problem, I was returning false on PreBind event when SDK_INT < KITKAT in my automatic transitions function:
binding.addOnRebindCallback(new OnRebindCallback()
{
#Override
public boolean onPreBind(ViewDataBinding binding)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
return false; //should be true, that's it
ViewGroup sceneRoot = (ViewGroup) binding.getRoot();
TransitionManager.beginDelayedTransition(sceneRoot, transition);
return true;
}
});

Set contentDescription for ActionBar's up button

I'm trying to customize the "Navigate up" default contentDescription that is associated with the up button of the ActionBar (I'm using ActionBarSherlock).
From ActionBarView's source:
public void setHomeButtonEnabled(boolean enable) {
mHomeLayout.setEnabled(enable);
mHomeLayout.setFocusable(enable);
// Make sure the home button has an accurate content description for accessibility.
if (!enable) {
mHomeLayout.setContentDescription(null);
} else if ((mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
mHomeLayout.setContentDescription(mContext.getResources().getText(
R.string.abs__action_bar_up_description));
} else {
mHomeLayout.setContentDescription(mContext.getResources().getText(
R.string.abs__action_bar_home_description));
}
}
so the key would be how to get a reference to mHomeLayout. getWindow().getDecorView().findViewById(android.R.id.home) is not working, as it's returning an ImageView.
How could I do?
Thanks ;)
layout
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/colorPrimary"
android:elevation="4dp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:layout_scrollFlags="scroll|enterAlways">
</android.support.v7.widget.Toolbar>
code
public Toolbar toolbar;
...
setContentView(layout);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(layoutTitle);
setSupportActionBar(toolbar);
...
getSupportActionBar().setHomeActionContentDescription("Go Back To XYZ Screen");
In xml, use "navigationContentDescription"
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_alignParentTop="true"
app:navigationContentDescription="#string/back"/>
Here how I can do what you need in a previous project :
((View) getWindow().getDecorView().findViewById(android.R.id.home).getParent().getParent()).setContentDescription("blablabla");
Using viewHierarchy plugin helps me to understand how ActionBar layout is build.
In case someone needs to set the ActionBar's home-button's content-description for UIAutomator, use
((View) getWindow().getDecorView().findViewById(android.R.id.home).getParent()).setContentDescription("MANUALLYSET-home-up");
and access the view in your UIAutomatorTestCase using
new UiObject(new UiSelector().description("MANUALLYSET-home-up").className("android.widget.FrameLayout"));
For some reason the additional *.getParent() did not work, instead Android uses some auto-generated content-description value for that parent which may differ in some Android versions (e.g. "app_name, Close navigation drawer" on KITKAT and "app_name, Navigate up" on JELLYBEAN). Accessing its child works too, fortunately.
Kind regards

Categories

Resources