PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/MonoIO/UI/ScheduleFragment.cs

https://gitlab.com/muthuprabhu/monodroid-samples
C# | 446 lines | 326 code | 82 blank | 38 comment | 25 complexity | 331638f950cc1d3552b9f9e1e22ef219 MD5 | raw file
  1. /*
  2. * Copyright 2011 Google Inc.
  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. using System;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using System.Text;
  20. using Android.App;
  21. using Android.Content;
  22. using Android.OS;
  23. using Android.Runtime;
  24. using Android.Util;
  25. using Android.Views;
  26. using Android.Widget;
  27. using Android.Support.V4.App;
  28. using MonoIO.Utilities;
  29. using Android.Text.Format;
  30. using Android.Database;
  31. using Fragment = Android.Support.V4.App.Fragment;
  32. using Android.Support.V4.View;
  33. using Math = Java.Lang.Math;
  34. using MonoIO.UI.Widget;
  35. using Android.Provider;
  36. using Uri = Android.Net.Uri;
  37. using Android.Graphics.Drawables;
  38. namespace MonoIO.UI
  39. {
  40. public class ScheduleFragment : Fragment, NotifyingAsyncQueryHandler.AsyncQueryListener, ObservableScrollView.OnScrollListener, View.IOnClickListener
  41. {
  42. public string TAG = "ScheduleFragment";
  43. /**
  44. * Flags used with {@link android.text.format.DateUtils#formatDateRange}.
  45. */
  46. private const FormatStyleFlags TIME_FLAGS = FormatStyleFlags.ShowDate | FormatStyleFlags.ShowWeekday | FormatStyleFlags.AbbrevWeekday;
  47. private long TUE_START = ParserUtils.ParseTime("2011-05-10T00:00:00.000-07:00");
  48. private long WED_START = ParserUtils.ParseTime("2011-05-11T00:00:00.000-07:00");
  49. const int DISABLED_BLOCK_ALPHA = 100;
  50. public Dictionary<string, int> TypeColumnMap = BuildTypeColumnMap();
  51. // TODO: show blocks that don't fall into columns at the bottom
  52. const string EXTRA_TIME_START = "monoio.extra.TIME_START";
  53. const string EXTRA_TIME_END = "monoio.extra.TIME_END";
  54. private NotifyingAsyncQueryHandler mHandler;
  55. private Workspace mWorkspace;
  56. private TextView mTitle;
  57. private int mTitleCurrentDayIndex = -1;
  58. private View mLeftIndicator;
  59. private View mRightIndicator;
  60. public SessionChangesObserver mSessionChangesObserver;
  61. public MyBroadcastReceiver mReceiver;
  62. /**
  63. * A helper class containing object references related to a particular day in the schedule.
  64. */
  65. private class Day : Java.Lang.Object
  66. {
  67. public ViewGroup RootView;
  68. public ObservableScrollView ScrollView;
  69. public View NowView;
  70. public BlocksLayout BlocksView;
  71. public int Index = -1;
  72. public String Label = null;
  73. public Uri BlocksUri = null;
  74. public long TimeStart = -1;
  75. public long TimeEnd = -1;
  76. }
  77. private List<Day> mDays = new List<Day>();
  78. private static Dictionary<string, int> BuildTypeColumnMap() {
  79. var map = new Dictionary<string, int>();
  80. map.Add(ParserUtils.BlockTypeFood, 0);
  81. map.Add(ParserUtils.BlockTypeSession, 1);
  82. map.Add(ParserUtils.BlockTypeOfficeHours, 2);
  83. return map;
  84. }
  85. public override void OnCreate (Bundle p0)
  86. {
  87. base.OnCreate (p0);
  88. mHandler = new NotifyingAsyncQueryHandler(Activity.ContentResolver, this);
  89. mSessionChangesObserver = new SessionChangesObserver(this);
  90. mReceiver = new MyBroadcastReceiver(this);
  91. SetHasOptionsMenu(true);
  92. //AnalyticsUtils.getInstance(Activity).trackPageView("/Schedule");
  93. }
  94. public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
  95. {
  96. ViewGroup root = (ViewGroup) inflater.Inflate(Resource.Layout.fragment_schedule, null);
  97. mWorkspace = root.FindViewById<Workspace>(Resource.Id.workspace);
  98. mTitle = root.FindViewById<TextView>(Resource.Id.block_title);
  99. mLeftIndicator = root.FindViewById(Resource.Id.indicator_left);
  100. mLeftIndicator.Touch += delegate(object sender, Android.Views.View.TouchEventArgs e) {
  101. if(((int)e.Event.Action & MotionEventCompat.ActionMask) == (int) MotionEventActions.Down)
  102. {
  103. mWorkspace.ScrollLeft();
  104. e.Handled = true;
  105. }
  106. e.Handled = false;
  107. };
  108. mLeftIndicator.Click += (sender, e) => {
  109. mWorkspace.ScrollLeft();
  110. };
  111. mRightIndicator = root.FindViewById(Resource.Id.indicator_right);
  112. mRightIndicator.Touch += delegate(object sender, Android.Views.View.TouchEventArgs e) {
  113. if(((int)e.Event.Action & MotionEventCompat.ActionMask) == (int) MotionEventActions.Down)
  114. {
  115. mWorkspace.ScrollRight();
  116. e.Handled = true;
  117. }
  118. e.Handled = false;
  119. };
  120. mRightIndicator.Click += (sender, e) => {
  121. mWorkspace.ScrollRight();
  122. };
  123. SetupDay(inflater, TUE_START);
  124. SetupDay(inflater, WED_START);
  125. UpdateWorkspaceHeader(0);
  126. mWorkspace.SetOnScrollListener(new MyScrollListener(this), true);
  127. return root;
  128. }
  129. public class MyScrollListener : Workspace.OnScrollListener
  130. {
  131. public ScheduleFragment _fragment;
  132. public MyScrollListener(ScheduleFragment fragment)
  133. {
  134. _fragment = fragment;
  135. }
  136. public void OnScroll (float screenFraction)
  137. {
  138. _fragment.UpdateWorkspaceHeader(Math.Round(screenFraction));
  139. }
  140. }
  141. public void UpdateWorkspaceHeader(int dayIndex) {
  142. if (mTitleCurrentDayIndex == dayIndex) {
  143. return;
  144. }
  145. mTitleCurrentDayIndex = dayIndex;
  146. Day day = mDays[dayIndex];
  147. mTitle.Text = day.Label;
  148. mLeftIndicator.Visibility = (dayIndex != 0) ? ViewStates.Visible : ViewStates.Invisible;
  149. mRightIndicator.Visibility = (dayIndex < mDays.Count - 1) ? ViewStates.Visible : ViewStates.Invisible;
  150. }
  151. private void SetupDay(LayoutInflater inflater, long startMillis)
  152. {
  153. Day day = new Day();
  154. // Setup data
  155. day.Index = mDays.Count;
  156. day.TimeStart = startMillis;
  157. day.TimeEnd = startMillis + DateUtils.DayInMillis;
  158. day.BlocksUri = ScheduleContract.Blocks.BuildBlocksBetweenDirUri(day.TimeStart, day.TimeEnd);
  159. // Setup views
  160. day.RootView = (ViewGroup) inflater.Inflate(Resource.Layout.blocks_content, null);
  161. day.ScrollView = day.RootView.FindViewById<ObservableScrollView>(Resource.Id.blocks_scroll);
  162. day.ScrollView.SetOnScrollListener(this);
  163. day.BlocksView = day.RootView.FindViewById<BlocksLayout>(Resource.Id.blocks);
  164. day.NowView = day.RootView.FindViewById(Resource.Id.blocks_now);
  165. day.BlocksView.DrawingCacheEnabled = true;
  166. day.BlocksView.AlwaysDrawnWithCacheEnabled = true;
  167. Java.Util.TimeZone.Default = UIUtils.ConferenceTimeZone;
  168. day.Label = DateUtils.FormatDateTime(Activity, startMillis, TIME_FLAGS);
  169. mWorkspace.AddView(day.RootView);
  170. mDays.Add(day);
  171. }
  172. public override void OnResume ()
  173. {
  174. base.OnResume ();
  175. // Since we build our views manually instead of using an adapter, we
  176. // need to manually requery every time launched.
  177. Requery();
  178. Activity.ContentResolver.RegisterContentObserver(ScheduleContract.Sessions.CONTENT_URI, true, mSessionChangesObserver);
  179. // Start listening for time updates to adjust "now" bar. TIME_TICK is
  180. // triggered once per minute, which is how we move the bar over time.
  181. IntentFilter filter = new IntentFilter();
  182. filter.AddAction(Intent.ActionTimeTick);
  183. filter.AddAction(Intent.ActionTimeChanged);
  184. filter.AddAction(Intent.ActionTimezoneChanged);
  185. Activity.RegisterReceiver(mReceiver, filter, null, new Handler());
  186. }
  187. private void Requery() {
  188. foreach (var day in mDays) {
  189. mHandler.StartQuery(0, day, day.BlocksUri, BlocksQuery.PROJECTION, null, null, ScheduleContract.Blocks.DEFAULT_SORT);
  190. }
  191. }
  192. public override void OnActivityCreated (Bundle p0)
  193. {
  194. base.OnActivityCreated (p0);
  195. Activity.RunOnUiThread(() => {
  196. UpdateNowView(true);
  197. });
  198. }
  199. public override void OnPause ()
  200. {
  201. base.OnPause ();
  202. Activity.UnregisterReceiver(mReceiver);
  203. Activity.ContentResolver.UnregisterContentObserver(mSessionChangesObserver);
  204. }
  205. #region AsyncQueryListener implementation
  206. public void OnQueryComplete (int token, Java.Lang.Object cookie, ICursor cursor)
  207. {
  208. if (Activity == null) {
  209. return;
  210. }
  211. Day day = (Day) cookie;
  212. // Clear out any existing sessions before inserting again
  213. day.BlocksView.RemoveAllBlocks();
  214. try {
  215. while (cursor.MoveToNext()) {
  216. string type = cursor.GetString(BlocksQuery.BLOCK_TYPE);
  217. // TODO: place random blocks at bottom of entire layout
  218. int column;
  219. try {
  220. column = TypeColumnMap[type];
  221. } catch {
  222. continue;
  223. }
  224. string blockId = cursor.GetString(BlocksQuery.BLOCK_ID);
  225. string title = cursor.GetString(BlocksQuery.BLOCK_TITLE);
  226. long start = cursor.GetLong(BlocksQuery.BLOCK_START);
  227. long end = cursor.GetLong(BlocksQuery.BLOCK_END);
  228. bool containsStarred = cursor.GetInt(BlocksQuery.CONTAINS_STARRED) != 0;
  229. BlockView blockView = new BlockView(Activity, blockId, title, start, end, containsStarred, column);
  230. int sessionsCount = cursor.GetInt(BlocksQuery.SESSIONS_COUNT);
  231. if (sessionsCount > 0) {
  232. blockView.Click += HandleClick;
  233. } else {
  234. blockView.Focusable = false;
  235. blockView.Enabled = false;
  236. LayerDrawable buttonDrawable = (LayerDrawable) blockView.Background;
  237. buttonDrawable.GetDrawable(0).SetAlpha(DISABLED_BLOCK_ALPHA);
  238. buttonDrawable.GetDrawable(2).SetAlpha(DISABLED_BLOCK_ALPHA);
  239. }
  240. day.BlocksView.AddView(blockView);
  241. }
  242. } finally {
  243. cursor.Close();
  244. }
  245. }
  246. void HandleClick (object view, EventArgs e)
  247. {
  248. Console.WriteLine ("On click!");
  249. if (view is BlockView) {
  250. String title = ((BlockView)view).Text;
  251. //AnalyticsUtils.getInstance(getActivity()).trackEvent(
  252. // "Schedule", "Session Click", title, 0);
  253. String blockId = ((BlockView) view).GetBlockId();
  254. Uri sessionsUri = ScheduleContract.Blocks.BuildSessionsUri(blockId);
  255. Intent intent = new Intent(Intent.ActionView, sessionsUri);
  256. intent.PutExtra(SessionsFragment.EXTRA_SCHEDULE_TIME_STRING, ((BlockView) view).GetBlockTimeString());
  257. ((BaseActivity) Activity).OpenActivityOrFragment(intent);
  258. }
  259. }
  260. #endregion
  261. #region IOnClickListener implementation
  262. public void OnClick (View view)
  263. {
  264. }
  265. #endregion
  266. /**
  267. * Update position and visibility of "now" view.
  268. */
  269. private bool UpdateNowView(bool forceScroll) {
  270. long now = UIUtils.GetCurrentTime(Activity);
  271. Day nowDay = null; // effectively Day corresponding to today
  272. foreach (var day in mDays)
  273. {
  274. if (now >= day.TimeStart && now <= day.TimeEnd) {
  275. nowDay = day;
  276. day.NowView.Visibility = ViewStates.Visible;
  277. } else {
  278. day.NowView.Visibility = ViewStates.Gone;
  279. }
  280. }
  281. if (nowDay != null && forceScroll) {
  282. // Scroll to show "now" in center
  283. mWorkspace.SetCurrentScreen(nowDay.Index);
  284. int offset = nowDay.ScrollView.Height / 2;
  285. nowDay.NowView.RequestRectangleOnScreen(new Android.Graphics.Rect(0, offset, 0, offset), true);
  286. nowDay.BlocksView.RequestLayout();
  287. return true;
  288. }
  289. return false;
  290. }
  291. #region OnScrollListener implementation
  292. public void OnScrollChanged (ObservableScrollView view)
  293. {
  294. // Keep each day view at the same vertical scroll offset.
  295. int scrollY = view.ScrollY;
  296. foreach (var day in mDays) {
  297. if (day.ScrollView != view) {
  298. day.ScrollView.ScrollTo(0, scrollY);
  299. }
  300. }
  301. }
  302. #endregion
  303. public override void OnCreateOptionsMenu (IMenu menu, MenuInflater inflater)
  304. {
  305. inflater.Inflate(Resource.Menu.schedule_menu_items, menu);
  306. base.OnCreateOptionsMenu (menu, inflater);
  307. }
  308. public override bool OnOptionsItemSelected (IMenuItem item)
  309. {
  310. if (item.ItemId == Resource.Id.menu_now) {
  311. if (!UpdateNowView(true)) {
  312. Toast.MakeText(Activity, Resource.String.toast_now_not_visible, ToastLength.Short).Show();
  313. }
  314. return true;
  315. }
  316. return base.OnOptionsItemSelected (item);
  317. }
  318. public class SessionChangesObserver : ContentObserver
  319. {
  320. ScheduleFragment _fragment;
  321. public SessionChangesObserver (ScheduleFragment fragment) : base(new Handler())
  322. {
  323. _fragment = fragment;
  324. }
  325. public override void OnChange (bool selfChange)
  326. {
  327. _fragment.Requery();
  328. }
  329. }
  330. public class MyBroadcastReceiver : BroadcastReceiver
  331. {
  332. ScheduleFragment _fragment;
  333. public MyBroadcastReceiver(ScheduleFragment fragment)
  334. {
  335. _fragment = fragment;
  336. }
  337. public override void OnReceive (Context context, Intent intent)
  338. {
  339. Log.Debug(_fragment.TAG, "onReceive time update");
  340. _fragment.UpdateNowView(false);
  341. }
  342. }
  343. private class BlocksQuery {
  344. public static String[] PROJECTION = {
  345. BaseColumns.Id,
  346. ScheduleContract.Blocks.BLOCK_ID,
  347. ScheduleContract.Blocks.BLOCK_TITLE,
  348. ScheduleContract.Blocks.BLOCK_START,
  349. ScheduleContract.Blocks.BLOCK_END,
  350. ScheduleContract.Blocks.BLOCK_TYPE,
  351. ScheduleContract.Blocks.SESSIONS_COUNT,
  352. ScheduleContract.Blocks.CONTAINS_STARRED,
  353. };
  354. public static int _ID = 0;
  355. public static int BLOCK_ID = 1;
  356. public static int BLOCK_TITLE = 2;
  357. public static int BLOCK_START = 3;
  358. public static int BLOCK_END = 4;
  359. public static int BLOCK_TYPE = 5;
  360. public static int SESSIONS_COUNT = 6;
  361. public static int CONTAINS_STARRED = 7;
  362. }
  363. }
  364. }