/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.appsimobile.appsii.module.home; import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; import com.appsimobile.appsii.DrawableCompat; import com.appsimobile.appsii.R; import java.util.TimeZone; /** * This widget display an analogic clock with two hands for hours and * minutes. * * @attr ref android.R.styleable#AnalogClock_dial * @attr ref android.R.styleable#AnalogClock_hand_hour * @attr ref android.R.styleable#AnalogClock_hand_minute */ public class AnalogClock extends View { private final Handler mHandler = new Handler(); Time mCalendar; String mTimezoneId; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { // only perform this when no specific timezone for this clock has been set if (mTimezoneId == null) { String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); } } onTimeChanged(); invalidate(); } }; private Drawable mHourHand; private Drawable mMinuteHand; private Drawable mDial; private int mDialWidth; private int mDialHeight; private boolean mAttached; private float mMinutes; private float mHour; private boolean mChanged; public AnalogClock(Context context) { this(context, null); } public AnalogClock(Context context, AttributeSet attrs) { this(context, attrs, 0); } public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr, 0); } private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { final Resources r = context.getResources(); final TypedArray a = context.obtainStyledAttributes( new int[]{R.attr.colorPrimary, R.attr.colorAccent, R.attr.appsiHomeWidgetPrimaryColor}); int primaryColor = a.getColor(0, Color.BLACK); int accentColor = a.getColor(1, Color.BLACK); int textColor = a.getColor(2, Color.BLACK); mDial = r.getDrawable(R.drawable.clock_dial); DrawableCompat.setTintColorCompat(mDial, textColor); mHourHand = r.getDrawable(R.drawable.clock_hour_hand); DrawableCompat.setTintColorCompat(mHourHand, textColor); mMinuteHand = r.getDrawable(R.drawable.clock_minute_hand); DrawableCompat.setTintColorCompat(mMinuteHand, primaryColor); a.recycle(); mCalendar = new Time(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs, defStyleAttr, defStyleRes); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mChanged = true; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); boolean changed = mChanged; if (changed) { mChanged = false; } int availableWidth = getRight() - getLeft(); int availableHeight = getBottom() - getTop(); int x = availableWidth / 2; int y = availableHeight / 2; final Drawable dial = mDial; int w = dial.getIntrinsicWidth(); int h = dial.getIntrinsicHeight(); boolean scaled = false; if (availableWidth < w || availableHeight < h) { scaled = true; float scale = Math.min((float) availableWidth / (float) w, (float) availableHeight / (float) h); canvas.save(); canvas.scale(scale, scale, x, y); } if (changed) { dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); } dial.draw(canvas); canvas.save(); canvas.rotate(mHour / 12.0f * 360.0f, x, y); final Drawable hourHand = mHourHand; if (changed) { w = hourHand.getIntrinsicWidth(); h = hourHand.getIntrinsicHeight(); hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); } hourHand.draw(canvas); canvas.restore(); canvas.save(); canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); final Drawable minuteHand = mMinuteHand; if (changed) { w = minuteHand.getIntrinsicWidth(); h = minuteHand.getIntrinsicHeight(); minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); } minuteHand.draw(canvas); canvas.restore(); if (scaled) { canvas.restore(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (!mAttached) { mAttached = true; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); // OK, this is gross but needed. This class is supported by the // remote views machanism and as a part of that the remote views // can be inflated by a context for another user without the app // having interact users permission - just for loading resources. // For exmaple, when adding widgets from a user profile to the // home screen. Therefore, we register the receiver as the current // user not the one the context is for. getContext().registerReceiver(mIntentReceiver, filter, null, mHandler); } // NOTE: It's safe to do these after registering the receiver since the receiver always runs // in the main thread, therefore the receiver can't run before this method returns. // The time zone may have changed while the receiver wasn't registered, so update the Time if (mTimezoneId == null) { mTimezoneId = TimeZone.getDefault().getID(); } mCalendar = new Time(mTimezoneId); // Make sure we update to the current time onTimeChanged(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mAttached) { getContext().unregisterReceiver(mIntentReceiver); mAttached = false; } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); float hScale = 1.0f; float vScale = 1.0f; if (widthMode != MeasureSpec.UNSPECIFIED && widthSize < mDialWidth) { hScale = (float) widthSize / (float) mDialWidth; } if (heightMode != MeasureSpec.UNSPECIFIED && heightSize < mDialHeight) { vScale = (float) heightSize / (float) mDialHeight; } float scale = Math.min(hScale, vScale); setMeasuredDimension(resolveSizeAndState((int) (mDialWidth * scale), widthMeasureSpec, 0), resolveSizeAndState((int) (mDialHeight * scale), heightMeasureSpec, 0)); } void onTimeChanged() { mCalendar.setToNow(); int hour = mCalendar.hour; int minute = mCalendar.minute; int second = mCalendar.second; mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; updateContentDescription(mCalendar); } private void updateContentDescription(Time time) { final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; Context context = getContext(); String contentDescription = DateUtils.formatDateTime(context, time.toMillis(false), flags); setContentDescription(contentDescription); } /** * Switches this clock to using the default time-zone. */ public void clearTimezone() { mTimezoneId = null; mCalendar = new Time(); onTimeChanged(); invalidate(); } /** * Switched this clock to a specific timezone */ public void setTimezone(String timezoneId) { mTimezoneId = timezoneId; mCalendar = new Time(timezoneId); onTimeChanged(); invalidate(); } }