androidwear-osambientwatchface

onAmbientModeChanged is not calling when in ambient watchface android wear


Recently i tried to implementing ambient mode in android wear watchface, after implement some coding, i found out that onAmbientModeChanged will not be called when turn into ambient mode (emulator), but turn to normal mode it will called twice, first trigger,the inAmbientMode is true, second trigger the inAmbientMode is false.

private class Engine extends CanvasWatchFaceService.Engine {
    ...
    ...
     @Override
     public void onAmbientModeChanged(boolean inAmbientMode) {
        Log.e(TAG, "isInAmbientMode():" + inAmbientMode);
        super.onAmbientModeChanged(inAmbientMode);
        if (mLowBitAmbient) {
            boolean antiAlias = !inAmbientMode;
            setPaintAntiAlias(antiAlias);
        }

        invalidate();
        updateTimer();
    }

    @override
    public void onDraw(Canvas canvas, , Rect bounds){
      ...
      for(int i=0;i<node.length;i++){
        //drawing canvas
      }
    }
}

AndroidManifest.xml

<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET"/>

What would it be the problem for not triggering when ambient is on? Is it a bug or i need to add some permission or class to trigger this? Any help would be appreciated.

EDIT: When ambient, the watchface won't redraw until it turn back to normal mode, then the watchface will draw the ambient watchface then only draw the normal watchface

After testing and debugging for something, i found out that the for loop is the main cause that didn't trigger the onAmbientModeChanged, after comment out the for loop, when ambient change, the onAmbientModeChanged will also trigger correctly but still don't know why it happen and solution.


Solution

  • I got a similar problem, where my Watch face would randomly not turn to Ambient mode after Marshmallow update, until the onTick() when the OS wakes the Watch Face for some milliseconds so it updates the time. I guess this problem is linked to more aggressive sleep policy on wearables, with Doze mode.

    I looked at the WatchFaceService code, decompiled by Android Studio and it's bundled Jetbrains java bytecode decompiler, on I saw that the onTick()method holds a non reference counted wakelock for 100ms. I tried to do the same in the onAmbientModeChanged() callback, and I never saw my Watch Face with it's intercative style in ambient mode.

    Here are the code snippets:

    In onCreate():

    final PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
    mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    mWakeLock.setReferenceCounted(false);
    

    In onAmbientModeChanged(…):

    mWakeLock.acquire(100L);
    

    You can try to fine tune the wakelock acquire time value by setting it to 10ms for example, and making it increment by 10ms each time to tap the Watch Face.

    I personally changed this value to something more appropriate as my Watch Face animates ambient mode switching.

    BTW, check that the code in the for loop which is repeated as many times as you told it is not too long to execute as performance on mobile devices is critical. I personally had a for loop in my Watch Face to draw ticks, but I made it smoother by first making it once and cache it in a Bitmap, and now, I'm going to use Canvas#drawLines(…) instead.