/// <summary> /// Unsubscribe from a quiz /// </summary> /// <param name="dbId">ID to unsub from</param> /// <returns>If the unsub was successful</returns> public static async Task <OperationReturnMessage> UnsubscribeFromQuizAsync(string dbId) { QuizInfo info = QuizRosterDatabase.GetQuizInfo(dbId); if (info.SyncStatus != (int)SyncStatusEnum.NotDownloadedAndNeedDownload) { string location = App.UserPath + "/" + info.Category + "/" + info.QuizName + "`" + info.AuthorName; if (Directory.Exists(location)) { Directory.Delete(location, true); } } OperationReturnMessage returnMessage = await Task.Run(async() => await ServerOperations.UnsubscribeToQuizAsync(dbId)); if (returnMessage == OperationReturnMessage.True) { QuizRosterDatabase.DeleteQuizInfo(dbId); return(returnMessage); } else if (returnMessage == OperationReturnMessage.FalseInvalidCredentials) { CredentialManager.IsLoggedIn = false; return(returnMessage); } else { return(returnMessage); } }
/// <summary> /// updates a quizinfo in a threaded realm /// </summary> /// <param name="editedQuizInfo">quiz to update</param> /// <param name="threadedRealm">threaded realm to use</param> private static void EditQuizInfo(QuizInfo editedQuizInfo, Realm threadedRealm) { threadedRealm.Write(() => { threadedRealm.Add(editedQuizInfo, update: true); }); }
/// <summary> /// Handle event when user clicks delete quiz button. /// </summary> /// <param name="e"></param> private async void ButtonDelete_Clicked(string deleteType, string DBId) { if (CredentialManager.IsLoggedIn) { bool unsubscribe = deleteType == "Unsubscribe"; string question; string message; if (unsubscribe) { question = "Are you sure you want to unsubscribe?"; message = "This will remove the copy from your device"; } else { question = "Are you sure you want to delete this quiz?"; message = "This will delete the copy on your device and in the cloud. This is not reversable."; } bool answer = await this.DisplayAlert(question, message, "Yes", "No"); if (answer) { // Acquire QuizInfo from roster QuizInfo rosterInfo = QuizRosterDatabase.GetQuizInfo(DBId); // Author string path = rosterInfo.RelativePath; // tell the roster that the quiz is deleted QuizInfo rosterInfoUpdated = new QuizInfo(rosterInfo) { IsDeletedLocally = true, LastModifiedDate = DateTime.Now.ToString() }; QuizRosterDatabase.EditQuizInfo(rosterInfoUpdated); // If connected, tell server to delete this quiz If not, it will tell server to delete next time it is connected in QuizRosterDatabase.UpdateLocalDatabase() if (CrossConnectivity.Current.IsConnected) { OperationReturnMessage returnMessage; if (unsubscribe) { returnMessage = await SubscribeUtils.UnsubscribeFromQuizAsync(DBId); } else { returnMessage = await ServerOperations.DeleteQuiz(DBId); } if (System.IO.Directory.Exists(path)) { Directory.Delete(path, true); } this.Setup(); } } } else { await this.DisplayAlert("Hold on!", "You must be create an account or log in before you can unsubscribe", "OK"); } }
/// <summary> /// Handle when user clicks edit button /// </summary> private async void ButtonEdit_Clicked(string DBId) { QuizInfo info = QuizRosterDatabase.GetQuizInfo(DBId); if (!CredentialManager.IsLoggedIn) { await this.DisplayAlert("Hold on!", "Before you can edit any quizzes, you have to login.", "Ok"); } else if (info.SyncStatus == (int)SyncStatusEnum.NotDownloadedAndNeedDownload) { await this.DisplayAlert("Hold on!", "This quiz isn't on your device, download it before you try to edit it", "Ok"); } else { CreateNewQuizPage quizPage = new CreateNewQuizPage(info); //Create the quizPage quizPage.SetQuizName(info.QuizName); Quiz quizDB = new Quiz(info.DBId); foreach (Question question in quizDB.GetQuestions()) { quizPage.AddNewQuestion(question); } await this.Navigation.PushAsync(quizPage); } }
/// <summary> /// Saves a quizinfo to the roster /// </summary> /// <param name="quizInfo">Quizinfo to save</param> public static void SaveQuizInfo(QuizInfo quizInfo) { RealmConfiguration threadConfig = App.realmConfiguration(RosterPath); Realm realmDB = Realm.GetInstance(threadConfig); realmDB.Write(() => { realmDB.Add(quizInfo); }); }
/// <summary> /// updates a quiz in the roster /// </summary> /// <param name="editedQuizInfo">quiz to update</param> public static void EditQuizInfo(QuizInfo editedQuizInfo) { RealmConfiguration threadConfig = App.realmConfiguration(RosterPath); Realm realmDB = Realm.GetInstance(threadConfig); realmDB.Write(() => { realmDB.Add(editedQuizInfo, update: true); }); realmDB.Dispose(); }
/// <summary> /// Send a quiz to the server with the current user credentials. /// </summary> /// <param name="relativeQuizPath">The path to the quiz on the current device.</param> /// <returns>A bool wrapped in a task representing whether the quiz successfully sent or not.</returns> public async static Task <bool> SendQuiz(string relativeQuizPath) { try { string realmFilePath = Directory.GetFiles(App.UserPath + relativeQuizPath, "*.realm").First(); Realm realm = Realm.GetInstance(App.realmConfiguration(realmFilePath)); QuizInfo info = realm.All <QuizInfo>().First(); if (await SendRealmFile(realmFilePath) != OperationReturnMessage.True) { throw new Exception(); } string[] imageFilePaths = Directory.GetFiles(App.UserPath + relativeQuizPath, "*.jpg"); for (int i = 0; i < imageFilePaths.Length; i++) { //[0] = path, [1] = fileName, [2] = dBId string fileName = imageFilePaths[i].Split('/').Last().Split('.').First(); string dbID = info.DBId; OperationReturnMessage message = await SendImageFile(imageFilePaths[i], fileName, dbID); if (message == OperationReturnMessage.False) { throw new Exception(); } } // When finished, confirm with server that quiz send has completed OperationReturnMessage finalizationMessage = (OperationReturnMessage)SendStringData( $"{info.DBId}`{info.LastModifiedDate}`{imageFilePaths.Length + 1}`" + $"{CredentialManager.Username}`{await SecureStorage.GetAsync("password")}`-", ServerRequestTypes.FinalizeQuizSend); if (finalizationMessage == OperationReturnMessage.True) { QuizInfo infoCopy = new QuizInfo(info) { SyncStatus = (int)SyncStatusEnum.Synced }; QuizRosterDatabase.EditQuizInfo(infoCopy); return(true); } else { throw new Exception(); } } catch (Exception ex) { string test = ex.ToString(); // Alert server that quiz send failed Delete records return(false); } }
/// <summary> /// Constructing from an already existing quiz /// </summary> /// <param name="originalName"> </param> /// <param name="originalAuthor"> </param> public CreateNewQuizPage(QuizInfo oldQuizInfo) { this.InitializeComponent(); this.SetUpBar(); this.originalCategory = oldQuizInfo.Category; this.originalAuthor = oldQuizInfo.AuthorName; this.originalName = oldQuizInfo.QuizName; this.PickerCategory.SelectedItem = oldQuizInfo.Category; this.originalQuizInfo = oldQuizInfo; }
/// <summary> /// Creates a new QuizInfo based on the information in another /// </summary> /// <param name="quizInfoToCopy">The quizInfo to make a copy of</param> public QuizInfo(QuizInfo quizInfoToCopy) { this.DBId = quizInfoToCopy.DBId; this.AuthorName = quizInfoToCopy.AuthorName; this.QuizName = quizInfoToCopy.QuizName; this.Category = quizInfoToCopy.Category; this.LastModifiedDate = quizInfoToCopy.LastModifiedDate; this.SyncStatus = quizInfoToCopy.SyncStatus; this.IsDeletedLocally = quizInfoToCopy.IsDeletedLocally; this.IsDeletedOnServer = quizInfoToCopy.IsDeletedOnServer; this.SubscriberCount = quizInfoToCopy.SubscriberCount; }
/// <summary> /// Deletes the quiz from the roster /// </summary> private async Task ButtonDelete_Clicked(string DBId) { bool answer = await this.DisplayAlert("Are you sure you want to delete this quiz?", "This will delete the copy on your device and in the cloud. This is not reversable.", "Yes", "No"); if (answer) { // Acquire QuizInfo from roster QuizInfo rosterInfo = QuizRosterDatabase.GetQuizInfo(DBId); string path = rosterInfo.RelativePath; if (rosterInfo != null) { string dbId = rosterInfo.DBId; // tell the roster that the quiz is deleted QuizInfo rosterInfoUpdated = new QuizInfo(rosterInfo) { IsDeletedLocally = true, LastModifiedDate = DateTime.Now.ToString() }; QuizRosterDatabase.EditQuizInfo(rosterInfoUpdated); // If connected, tell server to delete this quiz If not, it will tell server to delete next time it is connected in QuizRosterDatabase.UpdateLocalDatabase() OperationReturnMessage returnMessage = await ServerOperations.DeleteQuiz(dbId); if (returnMessage == OperationReturnMessage.True) { QuizRosterDatabase.DeleteQuizInfo(dbId); } if (System.IO.Directory.Exists(path)) { Directory.Delete(path, true); } this.QuizNumber.Text = "You have published a total of " + await Task.Run(() => ServerOperations.GetNumberOfQuizzesByAuthorName(CredentialManager.Username)) + " quizzes!"; } else { await DisplayAlert("Could not delete quiz", "This quiz could not be deleted at this time. Please try again later", "OK"); } // Setup Page again after deletion await this.UpdateProfilePageAsync(); } }
/// <summary> /// When a user wants to unsubscribe from a quiz /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void ImageButtonUnsubscribe_Clicked(object sender, EventArgs e) { if (CredentialManager.IsLoggedIn) { ImageButton button = (sender as ImageButton); string dbId = button.StyleId; bool answer = await this.DisplayAlert("Are you sure you want to unsubscribe?", "You will no longer get updates of this quiz", "Yes", "No"); if (answer) { ActivityIndicator indicatorSyncing = (button.Parent as StackLayout).Children[(int)SubscribeUtils.SubscribeType.Syncing] as ActivityIndicator; button.IsVisible = false; indicatorSyncing.IsVisible = true; indicatorSyncing.IsRunning = true; // get rosterInfo QuizInfo rosterInfo = QuizRosterDatabase.GetQuizInfo(dbId); // tell the roster that the quiz is deleted QuizInfo rosterInfoUpdated = new QuizInfo(rosterInfo) { IsDeletedLocally = true, LastModifiedDate = DateTime.Now.ToString() }; QuizRosterDatabase.EditQuizInfo(rosterInfoUpdated); OperationReturnMessage returnMessage = await SubscribeUtils.UnsubscribeFromQuizAsync(dbId); if (returnMessage == OperationReturnMessage.True) { (button.Parent as StackLayout).Children[(int)SubscribeUtils.SubscribeType.Subscribe].IsVisible = true; // add in subscribe button QuizRosterDatabase.DeleteQuizInfo(dbId); } else if (returnMessage == OperationReturnMessage.FalseInvalidCredentials) { button.IsVisible = true; await this.DisplayAlert("Invalid Credentials", "Your current login credentials are invalid. Please log in and try again.", "OK"); } else { button.IsVisible = true; await this.DisplayAlert("Unsubscribe Failed", "The unsubscription request could not be completed. Please try again.", "OK"); } indicatorSyncing.IsVisible = false; indicatorSyncing.IsRunning = false; } } else { await this.DisplayAlert("Hold on!", "Before you can subscribe to any quizzes, you have to login.", "Ok"); } }
/// <summary> /// Saves updates to a quizInfo /// </summary> /// <param name="editedQuizInfo">the new version of the quizinfo to save</param> public void EditQuizInfo(QuizInfo editedQuizInfo) { Realm realmDB = Realm.GetInstance(App.realmConfiguration(this.dbPath)); realmDB.Write(() => { realmDB.Add(editedQuizInfo, update: true); }); QuizInfo rosterCopy = new QuizInfo(editedQuizInfo) { SyncStatus = (int)SyncStatusEnum.NeedUpload // Default to 1, meaning "needs upload" in roster }; QuizRosterDatabase.EditQuizInfo(rosterCopy); }
/// <summary> /// Convert List of String arrays containing quiz data (from server) to QuizInfo objects /// </summary> /// <param name="quizData"></param> private static List <QuizInfo> ListOfDataToQuizInfo(List <string[]> quizDatas) { List <QuizInfo> toReturn = new List <QuizInfo>(); foreach (string[] quizData in quizDatas) { QuizInfo newInfo = new QuizInfo(); newInfo.DBId = quizData[0]; newInfo.AuthorName = quizData[1]; newInfo.QuizName = quizData[2]; newInfo.Category = quizData[3]; newInfo.SubscriberCount = int.Parse(quizData[4]); toReturn.Add(newInfo); } return(toReturn); }
/// <summary> /// Called when the user tries to download a quiz /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void SyncDownload_Clicked(object sender, EventArgs e) { // this.serverConnected = true; ImageButton button = sender as ImageButton; string dbId = button.ClassId; QuizInfo quizInfo = QuizRosterDatabase.GetQuizInfo(dbId); // DOES THIS WORK? while (quizInfo == null && Plugin.Connectivity.CrossConnectivity.Current.IsConnected) { await QuizRosterDatabase.UpdateLocalDatabaseAsync(); quizInfo = QuizRosterDatabase.GetQuizInfo(dbId); } string authorName = quizInfo.AuthorName; string quizName = quizInfo.QuizName; string category = quizInfo.Category; ActivityIndicator indicatorSyncing = (button.Parent as StackLayout).Children[(int)UISyncStatus.Syncing] as ActivityIndicator; string quizPath = button.ClassId; button.IsVisible = false; indicatorSyncing.IsVisible = true; indicatorSyncing.IsRunning = true; if (await Task.Run(() => ServerOperations.GetQuiz(dbId, category))) { ImageButton buttonSyncNoChange = (button.Parent as StackLayout).Children[(int)UISyncStatus.NoChange] as ImageButton; indicatorSyncing.IsVisible = false; buttonSyncNoChange.IsVisible = true; (((button.Parent as StackLayout).Parent as StackLayout).Parent as Frame).StyleId = "Local"; } else // If it failed to download { indicatorSyncing.IsVisible = false; button.IsVisible = true; await this.DisplayAlert("Quiz Download Failed", "This quiz could not be downloaded from the server. Please try again.", "OK"); } indicatorSyncing.IsRunning = false; await this.UpdateProfileContentAsync( this.SelectedCategory); }
/// <summary> /// Subscribes to a quiz given the ID /// </summary> /// <param name="dbId">the ID of the quiz to sub to</param> /// <param name="quizzesSearched">the quizzes currently displayed (used to get info about the quiz from the dbid)</param> /// <returns>If the subscription was successful</returns> public static async Task <OperationReturnMessage> SubscribeToQuizAsync(string dbId, List <QuizInfo> quizzesSearched) { if (QuizRosterDatabase.GetQuizInfo(dbId) == null) // make sure it isn't in yet { OperationReturnMessage returnMessage = await Task.Run(async() => await ServerOperations.SubscribeToQuiz(dbId)); if (returnMessage == OperationReturnMessage.True) { QuizInfo quiz = quizzesSearched.Where(quizInfo => quizInfo.DBId == dbId).First(); string lastModifiedDate = await Task.Run(() => ServerOperations.GetLastModifiedDate(dbId)); QuizInfo newInfo = new QuizInfo { DBId = quiz.DBId, AuthorName = quiz.AuthorName, QuizName = quiz.QuizName, Category = quiz.Category, LastModifiedDate = lastModifiedDate, SyncStatus = (int)SyncStatusEnum.NotDownloadedAndNeedDownload // 4 to represent not present in local directory and need download }; QuizRosterDatabase.SaveQuizInfo(newInfo); return(returnMessage); } else if (returnMessage == OperationReturnMessage.FalseInvalidCredentials) { CredentialManager.IsLoggedIn = false; return(returnMessage); } else { return(returnMessage); } } else { return(OperationReturnMessage.False); } }
/// <summary> /// Create a new quizinfo and adds it to the Quiz DB. /// A copy is stored in the device quiz roster. /// </summary> /// <param name="authorName">Author</param> /// <param name="quizName">Quiz name</param> /// <param name="category">Category</param> public void NewQuizInfo(string authorName, string quizName, string category) { QuizInfo newQuizInfo = new QuizInfo(authorName, quizName, category) { // Sync status is irrelevant in a Quiz Database's copy of the QuizInfo SyncStatus = -1 }; this.QuizInfo = newQuizInfo; Realm realmDB = Realm.GetInstance(App.realmConfiguration(this.dbPath)); realmDB.Write(() => { realmDB.Add(newQuizInfo); }); QuizInfo rosterCopy = new QuizInfo(newQuizInfo) { SyncStatus = (int)SyncStatusEnum.NeedUpload // Default to 1, meaning "needs upload" in roster }; QuizRosterDatabase.SaveQuizInfo(rosterCopy); }
/// <summary> /// Handle when user presses "three dot" icon on the quiz tab /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void ImageButtonMenu_Clicked(object sender, EventArgs e) { string DBId = ((ImageButton)sender).ClassId; QuizInfo quizInfo = QuizRosterDatabase.GetQuizInfo(DBId); string quizName = quizInfo.QuizName; string author = quizInfo.AuthorName; string deleteText = "Delete"; if (author != CredentialManager.Username) { deleteText = "Unsubscribe"; } string action = await this.DisplayActionSheet(quizName, "Back", null, deleteText, "Edit"); if (action == deleteText) { this.ButtonDelete_Clicked(((ImageButton)sender).StyleId, ((ImageButton)sender).ClassId); } else if (action == "Edit") { this.ButtonEdit_Clicked(DBId); } }
/// <summary> /// Download a quiz from the server. /// </summary> /// <param name="dBId">The DBId of the quiz to download</param> /// <param name="category">The category the quiz belongs in.</param> /// <returns></returns> public static bool GetQuiz(string dBId, string category) { // TO DO: CHECK IF quizName or category changed and save accordingly string tempPath = App.UserPath + "/" + "temp" + "/" + dBId + "/"; string tempFilePath = tempPath + "/" + realmFileExtension; // Store the realm file into a temporary directory in order the retrieve information from it // (Information regarding the images in order to retrieve them from the server) Directory.CreateDirectory(tempPath); byte[] realmFile = (byte[])SendStringData($"{dBId}/-", ServerRequestTypes.GetRealmFile); if (realmFile.Length > 0) { File.WriteAllBytes(tempFilePath, realmFile); } else { return(false); } // Retrieve info regarding the images from the realm file from its temporary location Realm realmDB = Realm.GetInstance(App.realmConfiguration(tempFilePath)); IQueryable <Question> questionsWithPictures = realmDB.All <Question>().Where(question => question.NeedsPicture); foreach (Question question in questionsWithPictures) { byte[] jpegFile = (byte[])SendStringData($"{question.QuestionId}/{dBId}/-", ServerRequestTypes.GetJPEGImage); string jpegFilePath = tempPath + "/" + question.QuestionId + jpegFileExtension; if (jpegFile.Length > 0) { File.WriteAllBytes(jpegFilePath, jpegFile); } else { return(false); } } QuizInfo info = realmDB.All <QuizInfo>().First(); string newQuizName = info.QuizName; string newCategory = info.Category; string newLastModfiedDate = info.LastModifiedDate; string quizPath = info.RelativePath; string realmFilePath = quizPath + "/" + info.DBId + realmFileExtension; Directory.CreateDirectory(quizPath); string[] imageFilePaths = Directory.GetFiles(tempPath, "*.jpg"); string[] realmFilePaths = Directory.GetFiles(tempPath, "*.realm"); foreach (string path in realmFilePaths) { File.Copy(path, quizPath + "/" + info.DBId + realmFileExtension, true); } foreach (string path in imageFilePaths) { string imageName = path.Split('/').Last(); File.Copy(path, quizPath + "/" + imageName, true); } if (category != newCategory) { DeleteDirectory(info.RelativePath, true); } DeleteDirectory(tempPath, true); QuizInfo infoCopy = new QuizInfo(QuizRosterDatabase.GetQuizInfo(dBId)) { SyncStatus = (int)SyncStatusEnum.Synced, QuizName = newQuizName, Category = newCategory, LastModifiedDate = newLastModfiedDate }; QuizRosterDatabase.EditQuizInfo(infoCopy); return(true); }
/// <summary> /// Updates the local database by syncing with the server /// </summary> /// <returns>an awaitable task for the completion of syncing</returns> public static async Task UpdateLocalDatabaseAsync() { if (App.Path != null && App.UserPath.Length > 2) { RealmConfiguration threadConfig = App.realmConfiguration(RosterPath); Realm threadInstance = Realm.GetInstance(threadConfig); List <QuizInfo> QuizInfos = new List <QuizInfo>(threadInstance.All <QuizInfo>()); if (CrossConnectivity.Current.IsConnected) { for (int i = 0; i < QuizInfos.Count(); i++) { if (CredentialManager.IsLoggedIn) { if (QuizInfos[i].IsDeletedLocally) { if (await ServerOperations.DeleteQuiz(QuizInfos[i].DBId) == OperationReturnMessage.True) { string toDeleteDBId = QuizInfos[i].DBId; QuizInfos.Remove(QuizInfos[i]); DeleteQuizInfo(toDeleteDBId); } } else if (QuizInfos[i].SyncStatus != (int)SyncStatusEnum.NotDownloadedAndNeedDownload) { string lastModifiedDate = ServerOperations.GetLastModifiedDate(QuizInfos[i].DBId); if (lastModifiedDate == "") // returns empty string could not reach server { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.Offline // 3 represents offline }; EditQuizInfo(copy, threadInstance); } else { // Server returns "false" if quiz is not already on the server if (lastModifiedDate == "false" || lastModifiedDate == null) { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.NeedUpload // 1 represents need upload }; EditQuizInfo(copy, threadInstance); } else { DateTime localModifiedDateTime = Convert.ToDateTime(QuizInfos[i].LastModifiedDate); DateTime serverModifiedDateTime = Convert.ToDateTime(lastModifiedDate); if (localModifiedDateTime > serverModifiedDateTime) { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.NeedUpload // 1 represents need upload }; EditQuizInfo(copy, threadInstance); } else if (localModifiedDateTime < serverModifiedDateTime) { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.NeedDownload // 0 represents needs download }; EditQuizInfo(copy, threadInstance); } else if (localModifiedDateTime == serverModifiedDateTime) { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.Synced // 2 represents in sync }; EditQuizInfo(copy, threadInstance); } } } } } else { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.Synced // 2 represents in sync }; EditQuizInfo(copy, threadInstance); } } string[] dbIds = new string[QuizInfos.Count]; for (int i = 0; i < dbIds.Length; i++) { dbIds[i] = QuizInfos[i].DBId; } List <string[]> missingQuizs = ServerOperations.GetMissingQuizzesByAuthorName(CredentialManager.Username, dbIds); foreach (string[] missingQuiz in missingQuizs) { QuizInfo info = new QuizInfo { DBId = missingQuiz[0], AuthorName = missingQuiz[1], QuizName = missingQuiz[2], Category = missingQuiz[3], LastModifiedDate = missingQuiz[4], SubscriberCount = int.Parse(missingQuiz[5]), SyncStatus = (int)SyncStatusEnum.NotDownloadedAndNeedDownload }; threadInstance.Write(() => { threadInstance.Add(info); }); } } else { for (int i = 0; i < QuizInfos.Count; i++) { QuizInfo copy = new QuizInfo(QuizInfos[i]) { SyncStatus = (int)SyncStatusEnum.Offline // 3 represents offline }; EditQuizInfo(copy, threadInstance); } } } }
/// <summary> /// Generate a frame (card-like button) for the UI based on a QuizInfo /// </summary> /// <param name="quizInfo">QuizInfo of the quiz to generate a frame for</param> /// <returns></returns> private Frame GenerateFrame(QuizInfo quizInfo) { Frame frame = new Frame() { VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.FillAndExpand, CornerRadius = 10 }; StackLayout frameStack = new StackLayout // 1st Child of frameLayout { FlowDirection = FlowDirection.LeftToRight, Orientation = StackOrientation.Vertical, Padding = 10 }; StackLayout topStack = new StackLayout { FlowDirection = FlowDirection.LeftToRight, Orientation = StackOrientation.Horizontal, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand }; Label title = new Label // 0 { Text = quizInfo.QuizName, FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), FontAttributes = FontAttributes.Bold, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand //, //HeightRequest = 45 }; topStack.Children.Add(title); // Add the sync buttons, We create one for each sync action to keep correct formatting and fix a sizing bug. ImageButton SyncOffline = new ImageButton // 1 { IsVisible = false, Source = "ic_cloud_off_black_48dp.png", HeightRequest = 25, WidthRequest = 25, BackgroundColor = Color.White, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, ClassId = quizInfo.DBId, StyleId = quizInfo.Category }; SyncOffline.Clicked += this.SyncOffline_Clicked; topStack.Children.Add(SyncOffline); ImageButton SyncUpload = new ImageButton // 2 { IsVisible = false, Source = "ic_cloud_upload_black_48dp.png", HeightRequest = 25, WidthRequest = 25, BackgroundColor = Color.White, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, ClassId = quizInfo.DBId, StyleId = quizInfo.Category }; SyncUpload.Clicked += this.SyncUpload_Clicked; topStack.Children.Add(SyncUpload); ImageButton SyncDownload = new ImageButton // 3 { IsVisible = false, Source = "ic_cloud_download_black_48dp.png", HeightRequest = 25, WidthRequest = 25, BackgroundColor = Color.White, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, ClassId = quizInfo.DBId, StyleId = quizInfo.Category }; SyncDownload.Clicked += this.SyncDownload_Clicked; topStack.Children.Add(SyncDownload); ImageButton SyncNoChange = new ImageButton // 4 { IsVisible = false, Source = "ic_cloud_done_black_48dp.png", HeightRequest = 25, WidthRequest = 25, BackgroundColor = Color.White, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, ClassId = quizInfo.DBId, StyleId = quizInfo.Category }; SyncNoChange.Clicked += this.SyncNoChange_Clicked; topStack.Children.Add(SyncNoChange); ActivityIndicator Syncing = new ActivityIndicator // 5 { IsVisible = false, Color = Color.Accent, HeightRequest = 25, WidthRequest = 25, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, }; topStack.Children.Add(Syncing); ImageButton imageButtonMenu = new ImageButton // 6 { Source = "ic_more_vert_black_48dp.png", HeightRequest = 35, WidthRequest = 35, BackgroundColor = Color.White, VerticalOptions = LayoutOptions.StartAndExpand, HorizontalOptions = LayoutOptions.End, ClassId = quizInfo.DBId }; imageButtonMenu.Clicked += this.ImageButtonMenu_Clicked; topStack.Children.Add(imageButtonMenu); if (CredentialManager.Username == quizInfo.AuthorName) { imageButtonMenu.StyleId = "Delete"; } else { imageButtonMenu.StyleId = "Unsubscribe"; } frameStack.Children.Add(topStack); BoxView Seperator = new BoxView // 1 { Color = Color.LightGray, CornerRadius = 1, HeightRequest = 2, WidthRequest = Application.Current.MainPage.Width - 75, HorizontalOptions = LayoutOptions.CenterAndExpand, VerticalOptions = LayoutOptions.CenterAndExpand }; frameStack.Children.Add(Seperator); Label Author = new Label // 2 { Text = "Created by: " + quizInfo.AuthorName, FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)), VerticalOptions = LayoutOptions.End, HorizontalOptions = LayoutOptions.StartAndExpand }; frameStack.Children.Add(Author); // The sync button thats active in the current frame ImageButton activeSync; if (quizInfo.SyncStatus == (int)SyncStatusEnum.Offline) // SyncOffline { SyncOffline.IsVisible = true; activeSync = SyncOffline; } else if (quizInfo.SyncStatus == (int)SyncStatusEnum.Synced) // SyncNoChange { SyncNoChange.IsVisible = true; activeSync = SyncNoChange; } else if (quizInfo.SyncStatus == (int)SyncStatusEnum.NeedUpload && quizInfo.AuthorName == CredentialManager.Username) // SyncUpload { SyncUpload.IsVisible = true; activeSync = SyncUpload; } else if (quizInfo.SyncStatus == (int)SyncStatusEnum.NeedDownload || quizInfo.SyncStatus == (int)SyncStatusEnum.NotDownloadedAndNeedDownload) // SyncDownload { SyncDownload.IsVisible = true; activeSync = SyncDownload; if (quizInfo.SyncStatus == (int)SyncStatusEnum.NotDownloadedAndNeedDownload) // Sync Download & notLocal yet { frame.StyleId = "notLocal"; } } else { SyncOffline.IsVisible = true; activeSync = SyncOffline; } TapGestureRecognizer recognizer = new TapGestureRecognizer(); recognizer.Tapped += async(object sender, EventArgs e) => { if (frame.StyleId != "notLocal") { frame.GestureRecognizers.Remove(recognizer); frame.BackgroundColor = Color.LightGray; Seperator.Color = Color.Gray; imageButtonMenu.BackgroundColor = Color.LightGray; activeSync.BackgroundColor = Color.LightGray; // Load the quiz associated with this DBId Quiz newQuiz = new Quiz(quizInfo.DBId); //await this.RemoveMenu(frameMenu); await this.Navigation.PushAsync(new Game(newQuiz)); frame.BackgroundColor = Color.Default; Seperator.Color = Color.LightGray; imageButtonMenu.BackgroundColor = Color.White; activeSync.BackgroundColor = Color.White; frame.GestureRecognizers.Add(recognizer); } else { await this.DisplayAlert("Hold on!", "In order to study with this quiz, you must download it first", "OK"); } }; frame.GestureRecognizers.Add(recognizer); frame.Content = frameStack; return(frame); }
/// <summary> /// Saves the user created quiz /// </summary> /// <param name="sender"> </param> /// <param name="e"> </param> private async void ButtonCreateQuiz_Clicked(object sender, EventArgs e) { this.Done.IsEnabled = false; if (string.IsNullOrWhiteSpace(this.EditorQuizName.Text)) { await this.DisplayAlert("Couldn't Create Quiz", "Please give your quiz a name.", "OK"); } else if (this.StackLayoutQuestionStack.Children.Count < 2) { await this.DisplayAlert("Couldn't Create Quiz", "Please create at least two questions", "OK"); } else if (this.PickerCategory.SelectedIndex == -1) { await this.DisplayAlert("Couldn't Create Quiz", "Please give your quiz a category", "OK"); } else { List <Question> previousQuestions = new List <Question>(); List <Question> NewQuestions = new List <Question>(); // A list of questions the user wants to add to the database // Loops through each question frame on the screen foreach (Frame frame in this.StackLayoutQuestionStack.Children) { // A list of all the children of the current frame IList <View> children = ((StackLayout)frame.Content).Children; Question addThis; //The answers to the question string[] answers = { ((Editor)children[2]).Text?.Trim(), //Correct answer ((Editor)children[3]).Text?.Trim(), // Incorect answer ((Editor)children[4]).Text?.Trim(), // Incorect answer ((Editor)children[5]).Text?.Trim() }; // Incorect answer // Checks if there is a question set if (string.IsNullOrWhiteSpace(((Editor)children[1]).Text)) { await this.DisplayAlert("Couldn't Create Quiz", "Every question must have a question set", "OK"); goto exit; } if (((ImageButton)children[6]).IsVisible) // if the question needs an image { addThis = new Question( ((Editor)children[1]).Text?.Trim(), // The Question ((ImageButton)children[6]).Source.ToString().Substring(6), // adds image using the image source answers) { NeedsPicture = true }; } else // if the question does not need an image { addThis = new Question( ((Editor)children[1]).Text?.Trim(), answers); } string questionType = ((Button)((StackLayout)children[0]).Children[0]).Text; // Sets the question type if (questionType == "Question Type: Multiple choice") { int size = 0; foreach (string answer in answers) { if (!string.IsNullOrWhiteSpace(answer)) { size++; } } if (size < 2 || string.IsNullOrWhiteSpace(answers[0])) { await this.DisplayAlert("Couldn't Create Quiz", "Mulitple choice questions must have a correct answer and at least one wrong answer", "OK"); goto exit; } addThis.QuestionType = 0; } else { if (string.IsNullOrWhiteSpace(answers[0])) { await this.DisplayAlert("Couldn't Create Quiz", "Text answer questions must have an answer", "OK"); goto exit; } if (questionType == "Question Type: Text answer") { addThis.QuestionType = 1; } else { addThis.QuestionType = 2; } } addThis.QuestionId = frame.StyleId; // Set the questionId NewQuestions.Add(addThis); } Quiz database; // If editing their own quiz if (this.originalQuizInfo != null && this.originalAuthor == CredentialManager.Username) { database = new Quiz(this.originalQuizInfo.DBId); // Set previousQuestions to the correct previous questions previousQuestions = database.GetQuestions(); } else // If new quiz { database = new Quiz( CredentialManager.Username, this.EditorQuizName.Text.Trim(), this.PickerCategory.Items[this.PickerCategory.SelectedIndex]); } // Add if it doesn't already exist, delete if it doesn't exist anymore, update the ones that need to be updated, and do nothing to the others if (previousQuestions.Count == 0 || originalAuthor != CredentialManager.Username) { // if the user created this for the first time // Save a new QuizInfo into the quiz database, which also adds this QuizInfo to the device quiz roster database.AddQuestions(NewQuestions.ToArray()); } else // edit { string currentDirectory = database.DBFolderPath; QuizInfo updatedQuizInfo = new QuizInfo(database.QuizInfo) { QuizName = this.EditorQuizName.Text?.Trim(), LastModifiedDate = DateTime.Now.ToString(), Category = this.PickerCategory.Items[this.PickerCategory.SelectedIndex] }; database.EditQuizInfo(updatedQuizInfo); if (currentDirectory != updatedQuizInfo.RelativePath) { Directory.CreateDirectory(updatedQuizInfo.RelativePath); Directory.Delete(updatedQuizInfo.RelativePath, true); Directory.Move(currentDirectory, updatedQuizInfo.RelativePath); } // Logic for how to save each question. for (int i = 0; i <= previousQuestions.Count() - 1; i++) { bool DBIdSame = true; // test each old question with each new question foreach (Question newQuestion in NewQuestions) { if (previousQuestions[i].QuestionId == newQuestion.QuestionId) { DBIdSame = true; // the same question, but changed, so update database.EditQuestion(newQuestion); NewQuestions.Remove(newQuestion); break; } else { DBIdSame = false; } } if (!DBIdSame) // if the question doesn't exist in the new list. delete it { database.DeleteQuestions(previousQuestions[i]); } } // Add all the questions that aren't eddited database.AddQuestions(NewQuestions.ToArray()); } File.Create(database.DBFolderPath + ".nomedia"); // Returns user to front page of QuizEditor and refreshed database await this.Navigation.PopAsync(true); } exit :; this.Done.IsEnabled = true; }