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.app.AlarmManager; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.SharedPreferences; 26 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.preference.PreferenceManager; 30 import android.view.LayoutInflater; 31 import android.view.MotionEvent; 32 import android.view.View; 33 import android.view.View.OnClickListener; 34 import android.view.View.OnTouchListener; 35 import android.view.ViewConfiguration; 36 import android.view.ViewGroup; 37 import android.widget.ImageButton; 38 import android.widget.ListView; 39 import android.widget.TextClock; 40 41 import com.android.deskclock.worldclock.CitiesActivity; 42 import com.android.deskclock.worldclock.WorldClockAdapter; 43 44 /** 45 * Fragment that shows the clock (analog or digital), the next alarm info and the world clock. 46 */ 47 public class ClockFragment extends DeskClockFragment implements OnSharedPreferenceChangeListener { 48 49 private static final String BUTTONS_HIDDEN_KEY = "buttons_hidden"; 50 private final static String TAG = "ClockFragment"; 51 52 private boolean mButtonsHidden = false; 53 private View mDigitalClock, mAnalogClock, mClockFrame, mHairline; 54 private WorldClockAdapter mAdapter; 55 private ListView mList; 56 private SharedPreferences mPrefs; 57 private String mDateFormat; 58 private String mDateFormatForAccessibility; 59 private String mDefaultClockStyle; 60 private String mClockStyle; 61 62 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 63 @Override 64 public void onReceive(Context context, Intent intent) { 65 String action = intent.getAction(); 66 boolean changed = action.equals(Intent.ACTION_TIME_CHANGED) 67 || action.equals(Intent.ACTION_TIMEZONE_CHANGED) 68 || action.equals(Intent.ACTION_LOCALE_CHANGED); 69 if (changed) { 70 Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); 71 if (mAdapter != null) { 72 // *CHANGED may modify the need for showing the Home City 73 if (mAdapter.hasHomeCity() != mAdapter.needHomeCity()) { 74 mAdapter.reloadData(context); 75 } else { 76 mAdapter.notifyDataSetChanged(); 77 } 78 // Locale change: update digital clock format and 79 // reload the cities list with new localized names 80 if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { 81 if (mDigitalClock != null) { 82 Utils.setTimeFormat( 83 (TextClock) (mDigitalClock.findViewById(R.id.digital_clock)), 84 (int) context.getResources(). 85 getDimension(R.dimen.main_ampm_font_size) 86 ); 87 } 88 mAdapter.loadCitiesDb(context); 89 mAdapter.notifyDataSetChanged(); 90 } 91 } 92 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 93 } 94 if (changed || action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) { 95 Utils.refreshAlarm(getActivity(), mClockFrame); 96 } 97 } 98 }; 99 100 private final Handler mHandler = new Handler(); 101 102 // Thread that runs on every quarter-hour and refreshes the date. 103 private final Runnable mQuarterHourUpdater = new Runnable() { 104 @Override 105 public void run() { 106 // Update the main and world clock dates 107 Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); 108 if (mAdapter != null) { 109 mAdapter.notifyDataSetChanged(); 110 } 111 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 112 } 113 }; 114 ClockFragment()115 public ClockFragment() { 116 } 117 118 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle)119 public View onCreateView(LayoutInflater inflater, ViewGroup container, 120 Bundle icicle) { 121 // Inflate the layout for this fragment 122 View v = inflater.inflate(R.layout.clock_fragment, container, false); 123 if (icicle != null) { 124 mButtonsHidden = icicle.getBoolean(BUTTONS_HIDDEN_KEY, false); 125 } 126 mList = (ListView) v.findViewById(R.id.cities); 127 mList.setDivider(null); 128 129 OnTouchListener longPressNightMode = new OnTouchListener() { 130 private float mMaxMovementAllowed = -1; 131 private int mLongPressTimeout = -1; 132 private float mLastTouchX 133 , 134 mLastTouchY; 135 136 @Override 137 public boolean onTouch(View v, MotionEvent event) { 138 if (mMaxMovementAllowed == -1) { 139 mMaxMovementAllowed = ViewConfiguration.get(getActivity()).getScaledTouchSlop(); 140 mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); 141 } 142 143 switch (event.getAction()) { 144 case (MotionEvent.ACTION_DOWN): 145 long time = Utils.getTimeNow(); 146 mHandler.postDelayed(new Runnable() { 147 @Override 148 public void run() { 149 startActivity(new Intent(getActivity(), ScreensaverActivity.class)); 150 } 151 }, mLongPressTimeout); 152 mLastTouchX = event.getX(); 153 mLastTouchY = event.getY(); 154 return true; 155 case (MotionEvent.ACTION_MOVE): 156 float xDiff = Math.abs(event.getX() - mLastTouchX); 157 float yDiff = Math.abs(event.getY() - mLastTouchY); 158 if (xDiff >= mMaxMovementAllowed || yDiff >= mMaxMovementAllowed) { 159 mHandler.removeCallbacksAndMessages(null); 160 } 161 break; 162 default: 163 mHandler.removeCallbacksAndMessages(null); 164 } 165 return false; 166 } 167 }; 168 169 // On tablet landscape, the clock frame will be a distinct view. Otherwise, it'll be added 170 // on as a header to the main listview. 171 mClockFrame = v.findViewById(R.id.main_clock_left_pane); 172 mHairline = v.findViewById(R.id.hairline); 173 if (mClockFrame == null) { 174 mClockFrame = inflater.inflate(R.layout.main_clock_frame, mList, false); 175 mHairline = mClockFrame.findViewById(R.id.hairline); 176 mHairline.setVisibility(View.VISIBLE); 177 mList.addHeaderView(mClockFrame, null, false); 178 } else { 179 mHairline.setVisibility(View.GONE); 180 // The main clock frame needs its own touch listener for night mode now. 181 v.setOnTouchListener(longPressNightMode); 182 } 183 mList.setOnTouchListener(longPressNightMode); 184 185 // If the current layout has a fake overflow menu button, let the parent 186 // activity set up its click and touch listeners. 187 View menuButton = v.findViewById(R.id.menu_button); 188 if (menuButton != null) { 189 setupFakeOverflowMenuButton(menuButton); 190 } 191 192 mDigitalClock = mClockFrame.findViewById(R.id.digital_clock); 193 mAnalogClock = mClockFrame.findViewById(R.id.analog_clock); 194 Utils.setTimeFormat((TextClock) (mDigitalClock.findViewById(R.id.digital_clock)), 195 (int) getResources().getDimension(R.dimen.main_ampm_font_size)); 196 View footerView = inflater.inflate(R.layout.blank_footer_view, mList, false); 197 mList.addFooterView(footerView, null, false); 198 mAdapter = new WorldClockAdapter(getActivity()); 199 if (mAdapter.getCount() == 0) { 200 mHairline.setVisibility(View.GONE); 201 } 202 mList.setAdapter(mAdapter); 203 204 mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 205 mDefaultClockStyle = getActivity().getResources().getString(R.string.default_clock_style); 206 return v; 207 } 208 209 @Override onResume()210 public void onResume() { 211 super.onResume(); 212 213 final DeskClock activity = (DeskClock) getActivity(); 214 if (activity.getSelectedTab() == DeskClock.CLOCK_TAB_INDEX) { 215 setFabAppearance(); 216 setLeftRightButtonAppearance(); 217 } 218 219 mPrefs.registerOnSharedPreferenceChangeListener(this); 220 mDateFormat = getString(R.string.abbrev_wday_month_day_no_year); 221 mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year); 222 223 Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); 224 // Besides monitoring when quarter-hour changes, monitor other actions that 225 // effect clock time 226 IntentFilter filter = new IntentFilter(); 227 filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 228 filter.addAction(Intent.ACTION_TIME_CHANGED); 229 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 230 filter.addAction(Intent.ACTION_LOCALE_CHANGED); 231 activity.registerReceiver(mIntentReceiver, filter); 232 233 // Resume can invoked after changing the cities list or a change in locale 234 if (mAdapter != null) { 235 mAdapter.loadCitiesDb(activity); 236 mAdapter.reloadData(activity); 237 } 238 // Resume can invoked after changing the clock style. 239 View clockView = Utils.setClockStyle(activity, mDigitalClock, mAnalogClock, 240 SettingsActivity.KEY_CLOCK_STYLE); 241 mClockStyle = (clockView == mDigitalClock ? 242 Utils.CLOCK_TYPE_DIGITAL : Utils.CLOCK_TYPE_ANALOG); 243 244 // Center the main clock frame if cities are empty. 245 if (getView().findViewById(R.id.main_clock_left_pane) != null && mAdapter.getCount() == 0) { 246 mList.setVisibility(View.GONE); 247 } else { 248 mList.setVisibility(View.VISIBLE); 249 } 250 mAdapter.notifyDataSetChanged(); 251 252 Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); 253 Utils.refreshAlarm(activity, mClockFrame); 254 } 255 256 @Override onPause()257 public void onPause() { 258 super.onPause(); 259 mPrefs.unregisterOnSharedPreferenceChangeListener(this); 260 Utils.cancelQuarterHourUpdater(mHandler, mQuarterHourUpdater); 261 Activity activity = getActivity(); 262 activity.unregisterReceiver(mIntentReceiver); 263 } 264 265 @Override onSaveInstanceState(Bundle outState)266 public void onSaveInstanceState(Bundle outState) { 267 outState.putBoolean(BUTTONS_HIDDEN_KEY, mButtonsHidden); 268 super.onSaveInstanceState(outState); 269 } 270 271 @Override onSharedPreferenceChanged(SharedPreferences prefs, String key)272 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 273 if (key == SettingsActivity.KEY_CLOCK_STYLE) { 274 mClockStyle = prefs.getString(SettingsActivity.KEY_CLOCK_STYLE, mDefaultClockStyle); 275 mAdapter.notifyDataSetChanged(); 276 } 277 } 278 279 @Override onFabClick(View view)280 public void onFabClick(View view) { 281 final Activity activity = getActivity(); 282 startActivity(new Intent(activity, CitiesActivity.class)); 283 } 284 285 @Override setFabAppearance()286 public void setFabAppearance() { 287 final DeskClock activity = (DeskClock) getActivity(); 288 if (mFab == null || activity.getSelectedTab() != DeskClock.CLOCK_TAB_INDEX) { 289 return; 290 } 291 mFab.setVisibility(View.VISIBLE); 292 mFab.setImageResource(R.drawable.ic_globe); 293 mFab.setContentDescription(getString(R.string.button_cities)); 294 } 295 296 @Override setLeftRightButtonAppearance()297 public void setLeftRightButtonAppearance() { 298 final DeskClock activity = (DeskClock) getActivity(); 299 if (mLeftButton == null || mRightButton == null || 300 activity.getSelectedTab() != DeskClock.CLOCK_TAB_INDEX) { 301 return; 302 } 303 mLeftButton.setVisibility(View.INVISIBLE); 304 mRightButton.setVisibility(View.INVISIBLE); 305 } 306 } 307