javaandroid-studioalarmmanagerwear-oswatch-face-api

Where to declair the alarm manager for updating a watchface in ambient mode?


I'm trying to add an AlarmManager to update a watch face on half minute intervals. This is for a ternary clock. This is my first time programming with Java or android studio.

I'm following a guide at https://developer.android.com/training/wearables/apps/always-on.html

The guide says to "declare the alarm manager and the pending intent in the onCreate() method of your activity"

Should I use

@Override
public Engine onCreateEngine() {
    return new Engine();
}

or

@Override
public Engine onCreateEngine() {
    return new Engine();
}

or should I start a new method or declare it elsewhere?

Currently I'm using

private class Engine extends CanvasWatchFaceService.Engine {
    final Handler mUpdateTimeHandler = new EngineHandler(this);

for most of my initialization.

This is my code without the alarm manager. The issue is that it must update during half minutes, because as balanced ternary the time should be to the nearest minute.

public class ternary extends CanvasWatchFaceService {
private static final Typeface NORMAL_TYPEFACE =
        Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);

private static final int MSG_UPDATE_TIME = 0;

@Override
public Engine onCreateEngine() {
    return new Engine();
}

private static class EngineHandler extends Handler {
    private final WeakReference<ternary.Engine> mWeakReference;

    public EngineHandler(ternary.Engine reference) {
        mWeakReference = new WeakReference<>(reference);
    }

    @Override
    public void handleMessage(Message msg) {
        ternary.Engine engine = mWeakReference.get();
        if (engine != null) {
            switch (msg.what) {
                case MSG_UPDATE_TIME:
                    engine.handleUpdateTimeMessage();
                    break;
            }
        }
    }
}

private class Engine extends CanvasWatchFaceService.Engine {
    final Handler mUpdateTimeHandler = new EngineHandler(this);
    boolean mRegisteredTimeZoneReceiver = false;
    Paint mBackgroundPaint;
    Paint mTextPaint;
    boolean mAmbient;
    Time mTime;
    final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mTime.clear(intent.getStringExtra("time-zone"));
            mTime.setToNow();
        }
    };
    int mTapCount;

    float mXOffset;
    float mYOffset;

    // adjust text size
    float textRatio = (float)1;  // 2/3;

    // make adjusted offset for hours
    float hrsIndent;
    float hrsIndentAdjust = textRatio * 55;

    // vertical offset for multiple lines
    float ySpacer = textRatio * 65;

    // first run.
    boolean yesFirstRun = true;
    // flag for seconds
    boolean yesSecs;
    // prior state of yesSecs
    boolean wasSecs = true;
    // flag for conservation mode (no seconds in ambient)
    boolean yesConcerve = false;
    // flag for allowing seconds
    boolean allowSecs = true;
    // for execution control
    boolean openGate = false;

    // counter for next draw
    int c = 0;
    // counter for time loops
    int k;
    boolean drawNow = true;
    // strings for draw
    String hrs = "";
    String mns = "";
    String sks = "";
    // register for milliseconds
    long millis = 0;
    // float for calculating trits from time.
    float tim = 0;
    // ints for minute and hour offsets.
    int minInt = 0;
    int hourInt = 0;

    // lists for time to trit for loop conversions.
    int [] trits3 = {9, 3, 1};
    int [] trits4 = {27, 9, 3, 1};

    // absolute count for trouble shooting
    // long x = 0;

    /**
     * Whether the display supports fewer bits for each color in ambient mode. When true, we
     * disable anti-aliasing in ambient mode.
     */
    boolean mLowBitAmbient;

    @Override
    public void onCreate(SurfaceHolder holder) {
        super.onCreate(holder);

        setWatchFaceStyle(new WatchFaceStyle.Builder(ternary.this)
                .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
                .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
                .setShowSystemUiTime(false)
                .setAcceptsTapEvents(true)
                .build());
        Resources resources = ternary.this.getResources();

        // shift y offset up
        mYOffset = -30 + resources.getDimension(R.dimen.digital_y_offset);

        mBackgroundPaint = new Paint();
        mBackgroundPaint.setColor(resources.getColor(R.color.background));

        mTextPaint = new Paint();
        mTextPaint = createTextPaint(resources.getColor(R.color.digital_text));

        mTime = new Time();
    }

    @Override
    public void onDestroy() {
        mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
        super.onDestroy();
    }

    private Paint createTextPaint(int textColor) {
        Paint paint = new Paint();
        paint.setColor(textColor);
        paint.setTypeface(NORMAL_TYPEFACE);
        paint.setAntiAlias(true);
        return paint;
    }

    @Override
    public void onVisibilityChanged(boolean visible) {
        super.onVisibilityChanged(visible);

        if (visible) {
            registerReceiver();

            // Update time zone in case it changed while we weren't visible.
            mTime.clear(TimeZone.getDefault().getID());
            mTime.setToNow();
        } else {
            unregisterReceiver();
        }

        // Whether the timer should be running depends on whether we're visible (as well as
        // whether we're in ambient mode), so we may need to start or stop the timer.
        updateTimer();
    }

    private void registerReceiver() {
        if (mRegisteredTimeZoneReceiver) {
            return;
        }
        mRegisteredTimeZoneReceiver = true;
        IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
        ternary.this.registerReceiver(mTimeZoneReceiver, filter);
    }

    private void unregisterReceiver() {
        if (!mRegisteredTimeZoneReceiver) {
            return;
        }
        mRegisteredTimeZoneReceiver = false;
        ternary.this.unregisterReceiver(mTimeZoneReceiver);
    }

    @Override
    public void onApplyWindowInsets(WindowInsets insets) {
        super.onApplyWindowInsets(insets);

        // Load resources that have alternate values for round watches.
        Resources resources = ternary.this.getResources();
        boolean isRound = insets.isRound();

        // shift offset 75 to the right
        mXOffset = 75 + resources.getDimension(isRound
                ? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset);
        float textSize = resources.getDimension(isRound
                ? R.dimen.digital_text_size_round : R.dimen.digital_text_size);

        // adjust hrs Indent to MXOffset
        hrsIndent = hrsIndentAdjust + mXOffset;

        // adjust size to textRatio
        mTextPaint.setTextSize(textSize * textRatio );
    }

    @Override
    public void onPropertiesChanged(Bundle properties) {
        super.onPropertiesChanged(properties);
        mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
    }

    @Override
    public void onTimeTick() {
        super.onTimeTick();
        invalidate();
    }

    @Override
    public void onAmbientModeChanged(boolean inAmbientMode) {
        super.onAmbientModeChanged(inAmbientMode);
        if (mAmbient != inAmbientMode) {
            mAmbient = inAmbientMode;
            if (mLowBitAmbient) {
                mTextPaint.setAntiAlias(!inAmbientMode);
            }
            invalidate();
        }

        // Whether the timer should be running depends on whether we're visible (as well as
        // whether we're in ambient mode), so we may need to start or stop the timer.
        updateTimer();
    }

    /**
     * Captures tap event (and tap type) and toggles the background color if the user finishes
     * a tap.
     */
    @Override
    public void onTapCommand(int tapType, int x, int y, long eventTime) {
        Resources resources = ternary.this.getResources();
        switch (tapType) {
            case TAP_TYPE_TOUCH:
                // The user has started touching the screen.
                break;
            case TAP_TYPE_TOUCH_CANCEL:
                // The user has started a different gesture or otherwise cancelled the tap.
                break;
            case TAP_TYPE_TAP:
                // The user has completed the tap gesture.
                mTapCount++;
                mBackgroundPaint.setColor(resources.getColor(mTapCount % 2 == 0 ?
                        R.color.background : R.color.background2));
                break;
        }
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas, Rect bounds) {
        // Greebo counter
        // x += 1;

        // seconds handling
        wasSecs = yesSecs;
        yesSecs = allowSecs && !isInAmbientMode();
        // for clearing seconds
        if (!yesSecs && wasSecs) { sks = ""; }

        // Draw at mid second
        if (c == 0 && yesSecs) {
            drawNow = true;
        } else {
            c = 0;
            // mid minute
            if (mTime.second == 30 || isInAmbientMode()) {
                drawNow = true;
            } else {
                // mid hour
                if (mTime.second == 0) {
                    if (mTime.minute == 30) {
                        drawNow = true;
                    } else {
                        // mid night
                        if (mTime.minute == 0) {
                            if (mTime.hour == 0) {
                                drawNow = true;
                            }
                        }
                    }
                }
            }
        }

        if (drawNow) {
            drawNow = false;

            mTime.setToNow();
            millis = System.currentTimeMillis() % 1000;

            // mid seconds
            if (yesSecs) { if (millis > 499) { c = 1; } }

            tim = (float)((mTime.minute * 60 + mTime.second) * 1000 + millis)/ 3600000;
            // hours past noon
            tim += mTime.hour - 12;

            // find hrs 9s, 3s, 1s.
            openGate = false;
            if (yesFirstRun || mTime.minute == 30){ openGate = true; }
            else { openGate = mTime.second == 0 && mTime.minute == 0 && mTime.hour == 0;}
            if (openGate) {
                hrs = "";
                hourInt = 0;
                // i is for item.
                for (int i : trits3) {
                    if (tim > ((float) i / 2)) {
                        tim -= i;
                        hourInt -= i;
                        hrs = hrs + "1";
                    } else {
                        if (tim < ((float) i / -2)) {
                            tim += i;
                            hourInt += i;
                            hrs = hrs + "¬";
                        } else {
                            hrs = hrs + "0";
                        }
                    }
                    // add space
                    if (i > 1) {hrs += " "; }
                }
            } else { tim += hourInt; }

            // minutes 27s, 9s, 3s, 1s
            openGate = false;
            if (yesFirstRun || mTime.second == 30 || isInAmbientMode()) {openGate = true; }
            else { openGate = mTime.second == 0 && (mTime.minute == 30
                    || (mTime.minute == 0 && mTime.hour == 0));}
            if (openGate) {
                mns = "";
                tim *= 60;
                minInt = 0;
                // i is for item.
                for (int i : trits4) {
                    if (tim > ((float) i / 2)) {
                        tim -= i;
                        if (yesSecs) {minInt -= i;}
                        mns = mns + "1";
                    } else {
                        if (tim < ((float) i / -2)) {
                            tim += i;
                            if (yesSecs) {minInt += i;}
                            mns = mns + "¬";
                        } else {
                            mns = mns + "0";
                        }
                    }
                    // add space
                    if (i > 1) {mns += " "; }
                }
            } else { if (yesSecs) { tim += minInt; tim *= 60; } }

            // seconds 27s, 9s, 3s, 1s
            if (yesSecs) {
                sks = "";
                tim *= 60;
                for (int i : trits4) {
                    if (tim > ((float) i / 2)) {
                        tim -= i;
                        sks = sks + "1";
                    } else {
                        if (tim < ((float) i / -2)) {
                            tim += i;
                            sks = sks + "¬";
                        } else {
                            sks = sks + "0";
                        }
                    }
                    // add space
                    if (i > 1) {sks += " "; }
                }
            }
        }

        // Draw the background.
        if (isInAmbientMode()) {
            canvas.drawColor(Color.BLACK);
        } else {
            canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
        }

        // draw hours
        canvas.drawText(hrs, hrsIndent, mYOffset - ySpacer, mTextPaint);
        // draw minutes
        canvas.drawText(mns, mXOffset, mYOffset, mTextPaint);
        // draw or clear seconds
        if (yesSecs || wasSecs) {canvas.drawText(sks, mXOffset, mYOffset + ySpacer , mTextPaint);}

        // show count and millis for greebo reduction.
        // canvas.drawText(String.format("%1$03d,%2$02d,%3$d", x % 1000, millis / 10, 0), mXOffset, mYOffset + 100, mTextPaint);
        //canvas.drawText(String.format("%$02d:%2$02d:%3$02d", mTime.hour, mTime.minute,
        //        mTime.second), mXOffset, mYOffset + 100, mTextPaint);

    }


    /**
     * Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently
     * or stops it if it shouldn't be running but currently is.
     */
    private void updateTimer() {
        mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME);
        if (shouldTimerBeRunning()) {
            mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME);
        }
    }

    /**
     * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should
     * only run when we're visible and in interactive mode.
     */
    private boolean shouldTimerBeRunning() {
        return isVisible() && !isInAmbientMode();
    }

    /**
     * Handle updating the time periodically in interactive mode.
     */
    private void handleUpdateTimeMessage() {
        invalidate();
        if (shouldTimerBeRunning()) {
            long timeMs = System.currentTimeMillis();
            long delayMs = INTERACTIVE_UPDATE_RATE_MS
                    - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
            mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
        }
    }
}

}


Solution

  • The "onCreate()" method seems to correlate with:

    public class ternary extends CanvasWatchFaceService

    Your declaration should be as follows:

    public class ternary extends CanvasWatchFaceService {
        private AlarmManager mAmbientStateAlarmManager;
        private PendingIntent mAmbientStatePendingIntent;
    

    And be sure to import android.app.AlarmManager and android.app.PendingIntent .