1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.deskclock; 18 19 import android.app.Activity; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.SharedPreferences; 25 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 26 import android.database.ContentObserver; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.preference.PreferenceManager; 30 import android.provider.Settings; 31 import android.view.LayoutInflater; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.View.OnTouchListener; 35 import android.view.ViewConfiguration; 36 import android.view.ViewGroup; 37 import android.widget.ListView; 38 import android.widget.TextClock; 39 40 import com.android.deskclock.alarms.AlarmNotifications; 41 import com.android.deskclock.worldclock.WorldClockAdapter; 42 43 /** 44 * Fragment that shows the clock (analog or digital), the next alarm info and the world clock. 45 */ 46 public class ClockFragment extends DeskClockFragment implements OnSharedPreferenceChangeListener { 47 48 private static final String BUTTONS_HIDDEN_KEY = "buttons_hidden"; 49 private final static String TAG = "ClockFragment"; 50 51 private boolean mButtonsHidden = false; 52 private View mDigitalClock, mAnalogClock, mClockFrame; 53 private WorldClockAdapter mAdapter; 54 private ListView mList; 55 private SharedPreferences mPrefs; 56 private String mDateFormat; 57 private String mDateFormatForAccessibility; 58 private String mDefaultClockStyle; 59 private String mClockStyle; 60 61 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 62 @Override 63 public void onReceive(Context context, Intent intent) { 64 String action = intent.getAction(); 65 boolean changed = action.equals(Intent.ACTION_TIME_CHANGED) 66 || action.equals(Intent.ACTION_TIMEZONE_CHANGED) 67 || action.equals(Intent.ACTION_LOCALE_CHANGED); 68 if (changed) { 69 Utils.updateDate(mDateFormat, mDateFormatForAccessibility,mClockFrame); 70 if (mAdapter != null) { 71 // *CHANGED may modify the need for showing the Home City 72 if (mAdapter.hasHomeCity() != mAdapter.needHomeCity()) { 73 mAdapter.reloadData(context); 74 } else { 75 mAdapter.notifyDataSetChanged(); 76 } 77 // Locale change: update digital clock format and 78 // reload the cities list with new localized names 79 if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { 80 if (mDigitalClock != null) { 81 Utils.setTimeFormat( 82 (TextClock)(mDigitalClock.findViewById(R.id.digital_clock)), 83 (int)context.getResources(). 84 getDimension(R.dimen.bottom_text_size)); 85 } 86 mAdapter.loadCitiesDb(context); 87 mAdapter.notifyDataSetChanged(); 88 } 89 } 90 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 91 } 92 if (changed || action.equals(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION)) { 93 Utils.refreshAlarm(getActivity(), mClockFrame); 94 } 95 } 96 }; 97 98 private final Handler mHandler = new Handler(); 99 100 private final ContentObserver mAlarmObserver = new ContentObserver(mHandler) { 101 @Override 102 public void onChange(boolean selfChange) { 103 Utils.refreshAlarm(ClockFragment.this.getActivity(), mClockFrame); 104 } 105 }; 106 107 // Thread that runs on every quarter-hour and refreshes the date. 108 private final Runnable mQuarterHourUpdater = new Runnable() { 109 @Override 110 public void run() { 111 // Update the main and world clock dates 112 Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); 113 if (mAdapter != null) { 114 mAdapter.notifyDataSetChanged(); 115 } 116 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 117 } 118 }; 119 120 public ClockFragment() { 121 } 122 123 @Override 124 public View onCreateView(LayoutInflater inflater, ViewGroup container, 125 Bundle icicle) { 126 // Inflate the layout for this fragment 127 View v = inflater.inflate(R.layout.clock_fragment, container, false); 128 if (icicle != null) { 129 mButtonsHidden = icicle.getBoolean(BUTTONS_HIDDEN_KEY, false); 130 } 131 mList = (ListView)v.findViewById(R.id.cities); 132 mList.setDivider(null); 133 134 OnTouchListener longPressNightMode = new OnTouchListener() { 135 private float mMaxMovementAllowed = -1; 136 private int mLongPressTimeout = -1; 137 private float mLastTouchX, mLastTouchY; 138 139 @Override 140 public boolean onTouch(View v, MotionEvent event) { 141 if (mMaxMovementAllowed == -1) { 142 mMaxMovementAllowed = ViewConfiguration.get(getActivity()).getScaledTouchSlop(); 143 mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); 144 } 145 146 switch (event.getAction()) { 147 case (MotionEvent.ACTION_DOWN): 148 long time = Utils.getTimeNow(); 149 mHandler.postDelayed(new Runnable() { 150 @Override 151 public void run() { 152 startActivity(new Intent(getActivity(), ScreensaverActivity.class)); 153 } 154 }, mLongPressTimeout); 155 mLastTouchX = event.getX(); 156 mLastTouchY = event.getY(); 157 return true; 158 case (MotionEvent.ACTION_MOVE): 159 float xDiff = Math.abs(event.getX()-mLastTouchX); 160 float yDiff = Math.abs(event.getY()-mLastTouchY); 161 if (xDiff >= mMaxMovementAllowed || yDiff >= mMaxMovementAllowed) { 162 mHandler.removeCallbacksAndMessages(null); 163 } 164 break; 165 default: 166 mHandler.removeCallbacksAndMessages(null); 167 } 168 return false; 169 } 170 }; 171 172 // On tablet landscape, the clock frame will be a distinct view. Otherwise, it'll be added 173 // on as a header to the main listview. 174 mClockFrame = v.findViewById(R.id.main_clock_left_pane); 175 if (mClockFrame == null) { 176 mClockFrame = inflater.inflate(R.layout.main_clock_frame, mList, false); 177 mList.addHeaderView(mClockFrame, null, false); 178 } else { 179 // The main clock frame needs its own touch listener for night mode now. 180 v.setOnTouchListener(longPressNightMode); 181 } 182 mList.setOnTouchListener(longPressNightMode); 183 184 // If the current layout has a fake overflow menu button, let the parent 185 // activity set up its click and touch listeners. 186 View menuButton = v.findViewById(R.id.menu_button); 187 if (menuButton != null) { 188 setupFakeOverflowMenuButton(menuButton); 189 } 190 191 mDigitalClock = mClockFrame.findViewById(R.id.digital_clock); 192 mAnalogClock = mClockFrame.findViewById(R.id.analog_clock); 193 Utils.setTimeFormat((TextClock)(mDigitalClock.findViewById(R.id.digital_clock)), 194 (int)getResources().getDimension(R.dimen.bottom_text_size)); 195 View footerView = inflater.inflate(R.layout.blank_footer_view, mList, false); 196 footerView.setBackgroundResource(R.color.blackish); 197 mList.addFooterView(footerView); 198 mAdapter = new WorldClockAdapter(getActivity()); 199 mList.setAdapter(mAdapter); 200 201 mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 202 mDefaultClockStyle = getActivity().getResources().getString(R.string.default_clock_style); 203 return v; 204 } 205 206 @Override 207 public void onResume () { 208 super.onResume(); 209 mPrefs.registerOnSharedPreferenceChangeListener(this); 210 mDateFormat = getString(R.string.abbrev_wday_month_day_no_year); 211 mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year); 212 213 Activity activity = getActivity(); 214 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 215 // Besides monitoring when quarter-hour changes, monitor other actions that 216 // effect clock time 217 IntentFilter filter = new IntentFilter(); 218 filter.addAction(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION); 219 filter.addAction(Intent.ACTION_TIME_CHANGED); 220 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 221 filter.addAction(Intent.ACTION_LOCALE_CHANGED); 222 activity.registerReceiver(mIntentReceiver, filter); 223 224 // Resume can invoked after changing the cities list or a change in locale 225 if (mAdapter != null) { 226 mAdapter.loadCitiesDb(activity); 227 mAdapter.reloadData(activity); 228 } 229 // Resume can invoked after changing the clock style. 230 View clockView = Utils.setClockStyle(activity, mDigitalClock, mAnalogClock, 231 SettingsActivity.KEY_CLOCK_STYLE); 232 mClockStyle = (clockView == mDigitalClock ? 233 Utils.CLOCK_TYPE_DIGITAL : Utils.CLOCK_TYPE_ANALOG); 234 235 // Center the main clock frame if cities are empty. 236 if (getView().findViewById(R.id.main_clock_left_pane) != null && mAdapter.getCount() == 0) { 237 mList.setVisibility(View.GONE); 238 } else { 239 mList.setVisibility(View.VISIBLE); 240 } 241 mAdapter.notifyDataSetChanged(); 242 243 Utils.updateDate(mDateFormat, mDateFormatForAccessibility,mClockFrame); 244 Utils.refreshAlarm(activity, mClockFrame); 245 activity.getContentResolver().registerContentObserver( 246 Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), 247 false, 248 mAlarmObserver); 249 } 250 251 @Override 252 public void onPause() { 253 super.onPause(); 254 mPrefs.unregisterOnSharedPreferenceChangeListener(this); 255 Utils.cancelQuarterHourUpdater(mHandler, mQuarterHourUpdater); 256 Activity activity = getActivity(); 257 activity.unregisterReceiver(mIntentReceiver); 258 activity.getContentResolver().unregisterContentObserver(mAlarmObserver); 259 } 260 261 @Override 262 public void onSaveInstanceState (Bundle outState) { 263 outState.putBoolean(BUTTONS_HIDDEN_KEY, mButtonsHidden); 264 super.onSaveInstanceState(outState); 265 } 266 267 @Override 268 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 269 if (key == SettingsActivity.KEY_CLOCK_STYLE) { 270 mClockStyle = prefs.getString(SettingsActivity.KEY_CLOCK_STYLE, mDefaultClockStyle); 271 mAdapter.notifyDataSetChanged(); 272 } 273 } 274 } 275