com.landenlabs.all_devtool.ClockFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.landenlabs.all_devtool.ClockFragment.java

Source

package com.landenlabs.all_devtool;

/*
 * Copyright (c) 2016 Dennis Lang (LanDen Labs) landenlabs@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * @author Dennis Lang  (3/21/2015)
 * @see http://LanDenLabs.com/
 *
 */

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.ToggleButton;

import com.landenlabs.all_devtool.receivers.AlarmReceiver;
import com.landenlabs.all_devtool.util.LLog;
import com.landenlabs.all_devtool.util.Ui;
import com.landenlabs.all_devtool.util.Utils;

import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/**
 * Display "Clock"  information.
 *
 * @author Dennis Lang
 *
 */
public class ClockFragment extends DevFragment implements View.OnClickListener {

    private final LLog m_log = LLog.DBG;
    public static String s_name = "Clock";
    SubMenu m_menu;
    Date m_date = new Date();
    TimeZone m_timeZone = TimeZone.getDefault();

    private View m_rootView;

    // ---- Alaram ----
    AlarmManager m_alarmManager;
    private PendingIntent m_pendingIntent;
    private TimePicker m_alarmTimePicker;
    private TextView m_alarmTextView;

    // ---- Clock / Times ----
    private TextView m_clockLocalTv;
    private TextView m_clockGmtTv;
    private TextView m_timePartsTv;
    private TextView m_dayLight;
    private TextView m_localeTv;
    private LinearLayout m_timeTopList;
    private LinearLayout m_timeBotList;

    // Timezone Daylight savings
    enum DaylightFilter {
        NoDS, HasDS, InDS
    }

    DaylightFilter m_daylightFilter = DaylightFilter.HasDS;

    // Additional times
    private TextView m_clockSysClkUpTm;
    private TextView m_clockSysClkReal;

    private static final DateFormat s_hour24Format = new SimpleDateFormat("HH:mm:ss.SSS");
    private static SimpleDateFormat s_time12Format = new SimpleDateFormat("MMM-dd hh:mm a");
    private static SimpleDateFormat s_time12ZoneFormat = new SimpleDateFormat("MM/dd/yyyy  hh:mm:ss a zzz");
    private static SimpleDateFormat s_time12GmtFormat = new SimpleDateFormat("MM/dd/yyyy  hh:mm:ss a 'GMT'");

    private static SimpleDateFormat s_time24Format = new SimpleDateFormat("MMM-dd HH:mm");
    private static SimpleDateFormat s_time24ZoneFormat = new SimpleDateFormat("MM/dd/yyyy  HH:mm:ss zzz");
    private static SimpleDateFormat s_time24GmtFormat = new SimpleDateFormat("MM/dd/yyyy  HH:mm:ss 'GMT'");

    private static SimpleDateFormat s_timeFormat = s_time12Format;
    private static SimpleDateFormat s_timeZoneFormat = s_time12ZoneFormat;
    private static SimpleDateFormat s_timeGmtFormat = s_time12GmtFormat;

    static {
        s_timeGmtFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    }

    // ---- Timer ----
    private boolean m_timerOn = false;
    private static final int REFRESH_MSEC = 1000;
    private Handler m_handler = new Handler();
    private Runnable m_updateElapsedTimeTask = new Runnable() {
        public void run() {
            updateClock();
            m_handler.postDelayed(this, REFRESH_MSEC); // Re-execute after xxx ms.
        }
    };

    public static DevFragment create() {
        return new ClockFragment();
    }

    // ============================================================================================
    // DevFragment methods

    @Override
    public String getName() {
        return s_name;
    }

    @Override
    public List<Bitmap> getBitmaps(int maxHeight) {
        List<Bitmap> bitmapList = new ArrayList<Bitmap>();
        bitmapList.add(Utils.grabScreen(this.getActivity()));
        return bitmapList;
    }

    @Override
    public void onSelected() {
        GlobalInfo.s_globalInfo.mainFragActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        GlobalInfo.s_globalInfo.mainFragActivity.getWindow()
                .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    // ============================================================================================
    // Fragment methods

    @SuppressWarnings("deprecation")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState != null)
            m_log.e("onViewCreated savedInstanceState");

        setHasOptionsMenu(true);

        m_rootView = inflater.inflate(R.layout.clock_tab, container, false);

        m_alarmTimePicker = Ui.viewById(m_rootView, R.id.alarmTimePicker);
        m_alarmTextView = Ui.viewById(m_rootView, R.id.alarmText);
        m_clockLocalTv = Ui.viewById(m_rootView, R.id.clockLocal);
        m_clockGmtTv = Ui.viewById(m_rootView, R.id.clockGmt);
        m_timePartsTv = Ui.viewById(m_rootView, R.id.timeParts);
        m_timeTopList = Ui.viewById(m_rootView, R.id.timeTopList);

        Ui.viewById(m_rootView, R.id.timezone_hasds).setOnClickListener(this);
        Ui.viewById(m_rootView, R.id.timezone_inds).setOnClickListener(this);
        Ui.viewById(m_rootView, R.id.timezone_nods).setOnClickListener(this);
        Ui.viewById(m_rootView, R.id.showTzMapBtn).setOnClickListener(this);

        // ---- Additional times ----
        m_clockSysClkUpTm = new TextView(m_timeTopList.getContext());
        m_timeTopList.addView(m_clockSysClkUpTm);
        m_clockSysClkReal = new TextView(m_timeTopList.getContext());
        m_timeTopList.addView(m_clockSysClkReal);
        m_localeTv = new TextView(m_timeTopList.getContext());
        m_timeTopList.addView(m_localeTv);

        m_timeBotList = Ui.viewById(m_rootView, R.id.timeBotList);
        m_dayLight = new TextView(m_timeBotList.getContext());
        m_timeBotList.addView(m_dayLight);

        // ---- Alaram ----
        ToggleButton alarmToggle = Ui.viewById(m_rootView, R.id.alarmToggle);
        alarmToggle.setOnClickListener(this);

        m_alarmTimePicker.setVisibility(alarmToggle.isChecked() ? View.VISIBLE : View.GONE);
        m_alarmManager = (AlarmManager) this.getActivity().getSystemService(Context.ALARM_SERVICE);

        return m_rootView;
    }

    @Override
    public void onResume() {
        super.onResume();
        m_handler.removeCallbacks(m_updateElapsedTimeTask);
        m_handler.postDelayed(m_updateElapsedTimeTask, 0);
    }

    @Override
    public void onPause() {
        super.onPause();
        m_handler.removeCallbacks(m_updateElapsedTimeTask);
    }

    @Override
    public void onStart() {
        super.onStart();
    }

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int pos = -1;
        int id = item.getItemId();
        switch (id) {
        case R.id.clock_12:
            s_timeFormat = s_time12Format;
            s_timeZoneFormat = s_time12ZoneFormat;
            s_timeGmtFormat = s_time12GmtFormat;
            break;
        case R.id.clock_24:
            s_timeFormat = s_time24Format;
            s_timeZoneFormat = s_time24ZoneFormat;
            s_timeGmtFormat = s_time24GmtFormat;
            break;

        default:
            break;
        }

        item.setChecked(true);
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        m_menu = menu.addSubMenu("Clock Format");
        inflater.inflate(R.menu.clock_menu, m_menu);
        m_menu.findItem(R.id.clock_12).setChecked(true);
    }

    // ============================================================================================
    // OnClick
    @Override
    public void onClick(View view) {

        int id = view.getId();
        switch (id) {
        case R.id.alarmToggle:
            toggleAlarm(((ToggleButton) view).isChecked());
            break;
        case R.id.showTzMapBtn:
            showTimezoneMap();
            break;
        case R.id.timezone_hasds:
            m_daylightFilter = DaylightFilter.HasDS;
            updateClock();
            break;
        case R.id.timezone_inds:
            m_daylightFilter = DaylightFilter.InDS;
            updateClock();
            break;
        case R.id.timezone_nods:
            m_daylightFilter = DaylightFilter.NoDS;
            updateClock();
            break;
        }
    }

    // ============================================================================================
    // Clock methods

    private static final long MIN_MILLI = 60 * 1000;
    private static final long HOUR_MILLI = MIN_MILLI * 60;

    private String getTzOffsetStr(TimeZone tz) {
        long tzOffMilli = tz.getRawOffset();
        long hours = tzOffMilli / HOUR_MILLI;
        long mins = Math.abs(tzOffMilli % HOUR_MILLI) / MIN_MILLI;

        return String.format("    GMT%+2d:%02d", hours, mins);
    }

    private String getTzDetailStr(TimeZone tz) {

        return String.format("%13s %s %s %s\n", getTzOffsetStr(tz), tz.getID(), tz.useDaylightTime() ? "HasDS" : "",
                tz.inDaylightTime(m_date) ? "InDs" : "");
    }

    private String getTzOffsetStr(TimeZone tz, SimpleDateFormat dateFormat) {
        dateFormat.setTimeZone(tz);
        return String.format("%13s %s %s\n", getTzOffsetStr(tz), dateFormat.format(m_date), tz.getID());
    }

    /**
     * Update clock on screen
     */
    private void updateClock() {
        m_date.setTime(System.currentTimeMillis());
        m_timeZone = TimeZone.getDefault();
        s_timeZoneFormat.setTimeZone(m_timeZone);
        String localTmStr = s_timeZoneFormat.format(m_date);

        m_clockLocalTv.setText(localTmStr);
        String gmtTmStr = s_timeGmtFormat.format(m_date);
        m_clockGmtTv.setText(gmtTmStr);

        long currDay = TimeUnit.DAYS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        m_timePartsTv.setText(String.format("Days since Jan 1 1970: %d", currDay));

        Locale ourLocale = Locale.getDefault();
        StringBuilder tzStr1 = new StringBuilder();
        StringBuilder tzStr2 = new StringBuilder();

        tzStr1.append(String.format("Locale %s\n", ourLocale.getDisplayName()));

        tzStr1.append(getTzDetailStr(m_timeZone));
        tzStr1.append("Daylight Savings:\n");

        // tzStr.append((m_timeZone.useDaylightTime() ? "Has" : "No") + " daylight savings\n");
        String ds_short = m_timeZone.getDisplayName(false, TimeZone.SHORT, ourLocale);
        tzStr1.append(
                String.format("    %s=%s\n", ds_short, m_timeZone.getDisplayName(false, TimeZone.LONG, ourLocale)));
        if (m_timeZone.useDaylightTime()) {
            String std_short = m_timeZone.getDisplayName(true, TimeZone.SHORT, ourLocale);
            tzStr1.append(String.format("    %s=%s\n", std_short,
                    m_timeZone.getDisplayName(true, TimeZone.LONG, ourLocale)));

            // ----
            // DateTimeZone zone1 = DateTimeZone.forID("Europe/London");
            // DateTimeZone zone2 = DateTimeZone.forID("America/New_York");

            DateTimeZone zone = DateTimeZone.forTimeZone(m_timeZone);
            DateTimeFormatter format = DateTimeFormat.mediumDateTime();

            long current = System.currentTimeMillis();
            for (int i = 0; i < 4; i++) {
                long next = zone.nextTransition(current);
                if (current == next) {
                    break;
                }

                tzStr1.append(String.format("    %s %s\n",
                        zone.isStandardOffset(next - 3600000) ? std_short : ds_short, format.print(next)));
                current = next;
            }
            m_localeTv.setText(tzStr1.toString());

            String[] ids = TimeZone.getAvailableIDs();
            if (ids != null && ids.length > 0) {
                switch (m_daylightFilter) {
                case NoDS:
                    tzStr2.append("TimeZones (no Daylight savings):\n");
                    break;
                case HasDS:
                    tzStr2.append("TimeZone (Has Daylight savings):\n");
                    break;
                case InDS:
                    tzStr2.append("TimeZone (In Daylight savings):\n");
                    break;
                }

                SparseIntArray zones = new SparseIntArray();
                for (int tzIdx = 0; tzIdx < ids.length; tzIdx++) {
                    TimeZone tz = TimeZone.getTimeZone(ids[tzIdx]);
                    boolean addTz = false;
                    switch (m_daylightFilter) {
                    case NoDS:
                        addTz = !tz.useDaylightTime();
                        break;
                    case HasDS:
                        addTz = tz.useDaylightTime() && !tz.inDaylightTime(m_date);
                        break;
                    case InDS:
                        addTz = tz.inDaylightTime(m_date);
                        break;
                    }
                    if (addTz) {
                        zones.put(tz.getRawOffset(), tzIdx);
                    }
                }

                for (int idx = 0; idx != zones.size(); idx++) {
                    TimeZone tz = TimeZone.getTimeZone(ids[zones.valueAt(idx)]);
                    tzStr2.append(getTzOffsetStr(tz, s_timeFormat));
                }
            }

        }
        m_dayLight.setText(tzStr2.toString());

        m_clockSysClkUpTm.setText("uptimeMillis:" + formatInterval(SystemClock.uptimeMillis()));
        m_clockSysClkReal.setText("elapsedRealtime:" + formatInterval(SystemClock.elapsedRealtime()));
    }

    /**
     * Format time interval
     * @param elapsedMillis
     * @return
     */
    private static String formatInterval(final long elapsedMillis) {
        final long day = TimeUnit.MICROSECONDS.toHours(elapsedMillis) / 24;
        final long hr = TimeUnit.MILLISECONDS.toHours(elapsedMillis) % 24;
        final long min = TimeUnit.MILLISECONDS.toMinutes(elapsedMillis) % 60;
        final long sec = TimeUnit.MILLISECONDS.toSeconds(elapsedMillis) % 60;
        final long ms = TimeUnit.MILLISECONDS.toMillis(elapsedMillis) % 1000;
        return String.format("%s %02d:%02d:%02d.%03d", (day == 0 ? "" : String.valueOf(day)), hr, min, sec, ms);
    }

    /**
     * Toogle alarm setting UI on/off and start/stop pending intent.
     * @param isOn
     */
    private void toggleAlarm(boolean isOn) {
        if (m_pendingIntent == null) {
            Intent myIntent = new Intent(GlobalInfo.s_globalInfo.mainFragActivity, AlarmReceiver.class);
            m_pendingIntent = PendingIntent.getBroadcast(GlobalInfo.s_globalInfo.mainFragActivity, 0, myIntent,
                    PendingIntent.FLAG_CANCEL_CURRENT);
        }

        if (isOn) {
            m_alarmTimePicker.setVisibility(View.VISIBLE);
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR_OF_DAY, m_alarmTimePicker.getCurrentHour());
            calendar.set(Calendar.MINUTE, m_alarmTimePicker.getCurrentMinute());
            calendar.set(Calendar.SECOND, 0);
            m_alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), m_pendingIntent);

            s_timeFormat.setTimeZone(m_timeZone);
            String alarmStr = s_timeFormat.format(calendar.getTime());
            Utils.sendNotification(this.getActivity(), Utils.CLOCK_NOTIFICATION_ID, "Alarm " + alarmStr);
            m_alarmTextView.setText(alarmStr);

            m_log.i("Alarm On " + alarmStr);
        } else {
            m_alarmTimePicker.setVisibility(View.GONE);
            m_alarmManager.cancel(m_pendingIntent);
            Utils.cancelNotification(this.getActivity(), Utils.CLOCK_NOTIFICATION_ID);
            m_alarmTextView.setText("");
            m_log.i("Alarm Off");
        }
    }

    private void showTimezoneMap() {
        String htmlStr = Utils.LoadData(getContext(), "timezone.html");

        Ui.showWebMessage(getContext(), Ui.HTML_CENTER_BOX, htmlStr);
        // Ui.showWebImage(getContext(), "file:///android_asset/world_timezone_map.png");
    }
}