// AnalogClockActivity.java Copyright (c) Kari Laitinen // http://www.naturalprogramming.com/ // 2012-08-23 File created. // 2016-03-31 Last modification. /* This program demonstrates: - the use of a thread - reading the current system time This Android application does not need an XML layout file. */ package analog.clock ; import android.app.Activity ; import android.os.Bundle ; import android.graphics.* ; // Classes Canvas, Color, Paint, RectF, etc. import android.view.* ; // Classes View, Display, WindowManager, etc. import android.content.Context ; import java.util.Calendar ; import java.util.GregorianCalendar ; final class AnalogClockView extends View implements Runnable { Thread animation_thread = null ; boolean thread_must_be_executed = false ; int view_width, view_height ; int clock_center_point_x, clock_center_point_y ; int hour_hand_length = 152 ; int minute_hand_length = 188 ; int second_hand_length = 200 ; Paint background_paint = new Paint() ; Paint paint_for_clock_dial = new Paint() ; Paint paint_for_text = new Paint() ; Paint paint_for_second_hand = new Paint() ; Paint paint_for_minute_hand = new Paint() ; Paint paint_for_hour_hand = paint_for_minute_hand ; public AnalogClockView( Context context, int given_display_width, int given_display_height ) { super( context ) ; view_width = given_display_width ; view_height = given_display_height ; clock_center_point_x = view_width / 2 ; clock_center_point_y = view_height / 2 ; // The lengths of the clock hands will be set so that the clock // fits nicely onto the available screen. int maximum_clock_hand_length = Math.min( view_width, view_height ) / 2 ; second_hand_length = maximum_clock_hand_length * 80 / 100 ; minute_hand_length = maximum_clock_hand_length * 76 / 100 ; hour_hand_length = maximum_clock_hand_length * 60 / 100 ; background_paint.setColor( Color.rgb( 210, 255, 210 ) ) ; // light green paint_for_clock_dial.setStyle( Paint.Style.FILL ) ; paint_for_text.setStyle( Paint.Style.STROKE ) ; paint_for_second_hand.setStyle( Paint.Style.STROKE ) ; paint_for_second_hand.setColor( Color.RED ) ; paint_for_minute_hand.setStyle( Paint.Style.STROKE ) ; paint_for_minute_hand.setColor( Color.rgb( 0x6B, 0x8E, 0x23 ) ) ; // olive color paint_for_minute_hand.setStrokeWidth( 4 ) ; } public void start_animation_thread() { if ( animation_thread == null ) { animation_thread = new Thread( this ) ; thread_must_be_executed = true ; animation_thread.start() ; } } public void stop_animation_thread() { if ( animation_thread != null ) { animation_thread.interrupt() ; thread_must_be_executed = false ; animation_thread = null ; } } public void run() { while ( thread_must_be_executed == true ) { try { Thread.sleep( 200 ) ; // Delay of 200 milliseconds. } catch ( InterruptedException caught_exception ) { thread_must_be_executed = false ; } // Here we must use the method postInvalidate() instead of // invalidate(). postInvalidate() must be used to invalidate // a View from a non-UI thread. postInvalidate() ; } } protected void onDraw( Canvas canvas ) { canvas.drawPaint( background_paint ) ; // This fills the entire canvas. String[] days_of_week = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } ; String[] names_of_months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } ; Calendar time_now = new GregorianCalendar() ; int current_year = time_now.get( Calendar.YEAR ) ; int current_day = time_now.get( Calendar.DAY_OF_MONTH ) ; int month_index = time_now.get( Calendar.MONTH ) ; int number_of_day_of_week = time_now.get( Calendar.DAY_OF_WEEK ) ; String current_month = names_of_months[ month_index ] ; String current_day_of_week = days_of_week[ number_of_day_of_week - 1 ] ; int current_hours = time_now.get( Calendar.HOUR_OF_DAY ) ; int current_minutes = time_now.get( Calendar.MINUTE ) ; int current_seconds = time_now.get( Calendar.SECOND ) ; int current_milliseconds = time_now.get( Calendar.MILLISECOND ) ; canvas.drawText( "" + current_day_of_week + ", " + current_month + " " + current_day + ", " + current_year , 20, 20, paint_for_text ) ; canvas.drawText( String.format( "%d:%02d:%02d", current_hours, current_minutes, current_seconds ), 20, 40, paint_for_text ) ; // Let's print a 10-point dot in the center of the clock. canvas.drawOval( new RectF( clock_center_point_x - 5, clock_center_point_y - 5, clock_center_point_x + 5, clock_center_point_y + 5 ), paint_for_clock_dial ) ; // The following loop prints dots on the clock dial. int dot_index = 0 ; while ( dot_index < 60 ) { double dot_angle = dot_index * Math.PI / 30 - Math.PI / 2 ; int dot_position_x = (int) (Math.cos( dot_angle ) * second_hand_length + clock_center_point_x ) ; int dot_position_y = (int) (Math.sin( dot_angle ) * second_hand_length + clock_center_point_y ) ; int dot_radius = 2 ; if ( ( dot_index % 5 ) == 0 ) { // Every 5th dot on the clock circle is a larger dot. dot_radius = 4 ; } canvas.drawOval( new RectF( dot_position_x - dot_radius, dot_position_y - dot_radius, dot_position_x + dot_radius, dot_position_y + dot_radius ), paint_for_clock_dial ) ; dot_index = dot_index + 1 ; } // For every hand of the clock, we'll first calculte the // angle of the hand in question. Then we can calculate the // the coordinates of the end point of the hand, and finally // we'll draw the hand. double hour_hand_angle = ( current_hours * 30 + current_minutes / 2 ) * Math.PI / 180 - Math.PI /2 ; int hour_hand_end_x = (int) ( Math.cos( hour_hand_angle ) * hour_hand_length + clock_center_point_x ) ; int hour_hand_end_y = (int) ( Math.sin( hour_hand_angle ) * hour_hand_length + clock_center_point_y ) ; canvas.drawLine( clock_center_point_x, clock_center_point_y, hour_hand_end_x, hour_hand_end_y, paint_for_hour_hand ) ; double minute_hand_angle = ( (double) current_minutes + (double) current_seconds / 60.0 ) * Math.PI / 30 - Math.PI /2 ; int minute_hand_end_x = (int) ( Math.cos( minute_hand_angle ) * minute_hand_length + clock_center_point_x ) ; int minute_hand_end_y = (int) ( Math.sin( minute_hand_angle ) * minute_hand_length + clock_center_point_y ) ; canvas.drawLine( clock_center_point_x, clock_center_point_y, minute_hand_end_x, minute_hand_end_y, paint_for_minute_hand ) ; double second_hand_angle = ((double) current_seconds + (double) current_milliseconds / 1000.0 ) * Math.PI / 30 - Math.PI /2 ; int second_hand_end_x = (int) ( Math.cos( second_hand_angle ) * second_hand_length + clock_center_point_x ) ; int second_hand_end_y = (int) ( Math.sin( second_hand_angle) * second_hand_length + clock_center_point_y ) ; canvas.drawLine( clock_center_point_x, clock_center_point_y, second_hand_end_x, second_hand_end_y, paint_for_second_hand ) ; } } public class AnalogClockActivity extends Activity { AnalogClockView analog_clock_view; public void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ) ; Display display = ( (WindowManager) getSystemService( Context.WINDOW_SERVICE )).getDefaultDisplay() ; int display_width = display.getWidth() ; int display_height = display.getHeight() ; analog_clock_view = new AnalogClockView( this, display_width, display_height ) ; setContentView( analog_clock_view ) ; } public void onStart() { super.onStart() ; analog_clock_view.start_animation_thread() ; } public void onStop() { super.onStop() ; analog_clock_view.stop_animation_thread() ; } } /* NOTES: A common mistake made by new Android developers is to use the width and height of a view inside its constructor. When a view’s constructor is called, Android doesn’t know yet how big the view will be, so the sizes are set to zero. The real sizes are calculated during the layout stage, which occurs after construction but before anything is drawn. You can use the onSizeChanged( )method to be notified of the values when they are known, or you can use the getWidth( ) and getHeight( )methods later, such as in the onDraw( ) method. (This was written by Ed Burnette at http://www.coderanch.com) */