void JoinClicked(PlatformButton button) { if (ValidateInput( )) { //hack - Can't figure out WHY the join button isn't in the proper Z order on the Nexus 7. // but I just don't care right now. So hide it and unhide it. JoinButton.Hidden = true; EnableControls(false); BlockerView.Show( ); MobileAppApi.JoinGroup(GroupID, FirstName.Text, LastName.Text, SpouseName.Text, Email.Text, CellPhone.Text.AsNumeric( ), delegate(System.Net.HttpStatusCode statusCode, string statusDescription) { BlockerView.Hide( ); if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { ResultView.Show(RegisterStrings.RegisterStatus_Success, PrivateControlStylingConfig.Result_Symbol_Success, string.Format(ConnectStrings.JoinGroup_RegisterSuccess, GroupTitle.Text), GeneralStrings.Done); } else { ResultView.Show(RegisterStrings.RegisterStatus_Failed, PrivateControlStylingConfig.Result_Symbol_Failed, ConnectStrings.JoinGroup_RegisterFailed, GeneralStrings.Done); } }); } }
public void BindRockAccount(string username, string password, BindResult bindResult) { MobileAppApi.Login(username, password, delegate(System.Net.HttpStatusCode statusCode, string statusDescription) { // if we received Ok (nocontent), we're logged in. if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { UserID = username; RockPassword = password; AccessToken = ""; AccountType = BoundAccountType.Rock; // save! SaveToDevice( ); bindResult(true); } else { bindResult(false); } }); }
void PerformRequest( ) { // do we need an impersonation token? if (IncludeImpersonationToken) { MobileAppApi.TryGetImpersonationToken( delegate(string impersonationToken) { // put the platform we're running string fullUrl = Rock.Mobile.Util.Strings.Parsers.AddParamToURL(DisplayUrl, PrivateGeneralConfig.MobilePlatform); // URL encode the value NSString encodedUrlString = fullUrl.UrlEncode( ); // if we got a token, append it NSUrl encodedUrl = null; if (string.IsNullOrEmpty(impersonationToken) == false) { encodedUrl = new NSUrl(encodedUrlString + "&" + impersonationToken); } else { encodedUrl = new NSUrl(encodedUrlString); } LaunchWebview(encodedUrl); }); } else { NSString encodedUrlString = DisplayUrl.UrlEncode( ); NSUrl encodedUrl = new NSUrl(encodedUrlString); LaunchWebview(encodedUrl); } }
void RegisterUser() { if (State == RegisterState.None) { // make sure they entered all required fields if (ValidateInput( )) { ToggleControls(false); ProgressBarBlocker.Visibility = ViewStates.Visible; State = RegisterState.Trying; // create a new user and submit them Rock.Client.Person newPerson = new Rock.Client.Person(); // copy all the edited fields into the person object newPerson.Email = EmailText.Text; // set the nickName AND firstName to NickName newPerson.NickName = NickNameText.Text; newPerson.FirstName = NickNameText.Text; newPerson.LastName = LastNameText.Text; MobileAppApi.RegisterNewUser(UserNameText.Text, PasswordText.Text, NickNameText.Text, LastNameText.Text, EmailText.Text, CellPhoneText.Text, delegate(System.Net.HttpStatusCode statusCode, string statusDescription) { ProgressBarBlocker.Visibility = ViewStates.Gone; // scroll to the top so the Result is visible ScrollView.Post(new Action(delegate { ScrollView.ScrollTo(0, 0); })); if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { ProfileAnalytic.Instance.Trigger(ProfileAnalytic.Register); State = RegisterState.Success; ResultView.Show(RegisterStrings.RegisterStatus_Success, PrivateControlStylingConfig.Result_Symbol_Success, RegisterStrings.RegisterResult_Success, GeneralStrings.Done); } else { State = RegisterState.Fail; ResultView.Show(RegisterStrings.RegisterStatus_Failed, PrivateControlStylingConfig.Result_Symbol_Failed, statusDescription, GeneralStrings.Done); } ResultView.SetBounds(new System.Drawing.RectangleF(0, 0, NavbarFragment.GetFullDisplayWidth( ), this.Resources.DisplayMetrics.HeightPixels)); }); } } }
public void GetPersonData(HttpRequest.RequestResult onResult) { MobileAppApi.GetPersonData(UserID, delegate(MobileAppApi.PersonData personData, HttpStatusCode statusCode) { if (personData != null) { // take the person (clear out the cell numbers since we manage those seperately) Person = personData.Person; Person.PhoneNumbers = null; // search for a phone number (which should match whatever we already have, unless this is a new login) if (personData.CellPhoneNumber != null) { _CellPhoneNumber = personData.CellPhoneNumber; HasCellNumber = true; } else { // no longer has a phone number, so clear it _CellPhoneNumber = new PhoneNumber( ); SetPhoneNumberDigits(""); HasCellNumber = false; } // we're always safe to take family--it cannot be null PrimaryFamily = personData.Family; // only take the address if it's valid. otherwise, we want // to use the default, empty one. if (personData.FamilyAddress != null) { PrimaryAddress = personData.FamilyAddress; } // set the person's current actions IsBaptised = personData.IsBaptised; IsERA = personData.IsERA; IsGiving = personData.IsGiving; TakenStartingPoint = personData.TakenStartingPoint; IsMember = personData.IsMember; IsServing = personData.IsServing; IsPeerLearning = personData.IsPeerLearning; IsMentored = personData.IsMentored; IsTeaching = personData.IsTeaching; // save! SaveToDevice( ); } if (onResult != null) { onResult(statusCode, ""); } }); }
void RegisterUser() { if (State == RegisterState.None) { // make sure they entered all required fields if (ValidateInput( )) { BlockerView.Show( delegate { // force the UI to scroll back up ScrollView.ContentOffset = CGPoint.Empty; ScrollView.ScrollEnabled = false; State = RegisterState.Trying; // create a new user and submit them Rock.Client.Person newPerson = new Rock.Client.Person(); // copy all the edited fields into the person object newPerson.Email = EmailText.Field.Text; // set both the nick name and first name to NickName newPerson.NickName = NickNameText.Field.Text; newPerson.FirstName = NickNameText.Field.Text; newPerson.LastName = LastNameText.Field.Text; MobileAppApi.RegisterNewUser(UserNameText.Field.Text, PasswordText.Field.Text, NickNameText.Field.Text, LastNameText.Field.Text, EmailText.Field.Text, CellPhoneText.Field.Text, delegate(System.Net.HttpStatusCode statusCode, string statusDescription) { if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { ProfileAnalytic.Instance.Trigger(ProfileAnalytic.Register); State = RegisterState.Success; ResultView.Show(RegisterStrings.RegisterStatus_Success, PrivateControlStylingConfig.Result_Symbol_Success, RegisterStrings.RegisterResult_Success, GeneralStrings.Done); } else { State = RegisterState.Fail; ResultView.Show(RegisterStrings.RegisterStatus_Failed, PrivateControlStylingConfig.Result_Symbol_Failed, statusDescription, GeneralStrings.Done); } BlockerView.Hide(null); }); }); } } }
/// <summary> /// Wrapper function for getting the basic things we need at launch (campuses, prayer categories, news, notes, etc.) /// If for some reason one of these fails, they will be called independantly by the appropriate systems /// (So if NoteDB fails, GetNoteDB will be called by Messages when the user taps on it) /// </summary> public void GetLaunchData(int?personId, HttpRequest.RequestResult launchDataResult) { Rock.Mobile.Util.Debug.WriteLine("Get LaunchData"); MobileAppApi.Get_LaunchData( delegate(System.Net.HttpStatusCode statusCode, string statusDescription, MobileAppApi.LaunchData launchData) { if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true && launchData != null) { Data.Campuses = launchData.Campuses; Data.PrayerCategories = launchData.PrayerCategories; // is our version up-to-date? if (GeneralConfig.Version < launchData.MobileAppVersion) { // nope, so flag that and we can remind people to upgrade. Data.NeedsUpgrade = true; } else { Data.NeedsUpgrade = false; } // now get the news. GetNews(delegate { // now get the campaign for the user GetPECampaign(personId, delegate { // chain any other required launch data actions here. Rock.Mobile.Util.Debug.WriteLine("Get LaunchData DONE"); // notify the caller now that we're done if (launchDataResult != null) { // send OK, because whether we failed or not, the caller doessn't need to care. launchDataResult(System.Net.HttpStatusCode.OK, ""); } }); }); } else { // notify the caller now that we're done if (launchDataResult != null) { // send failed, and we are not gonna move on. launchDataResult(System.Net.HttpStatusCode.BadGateway, ""); } } }); }
public void UpdateOrAddPhoneNumber(HttpRequest.RequestResult phoneResult) { // if they currently have a cell number, OR their number is non-empty if (HasCellNumber == true || string.IsNullOrEmpty(_CellPhoneNumber.Number) == false) { // if they DON'T have a cell number, then yes, this will be a new number. bool addNewPhoneNumber = !HasCellNumber; MobileAppApi.UpdateOrAddPhoneNumber(Person, _CellPhoneNumber, addNewPhoneNumber, delegate(System.Net.HttpStatusCode statusCode, string statusDescription, PhoneNumber model) { if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { // if we got back a model with an actual number, it's updated. if (model != null && string.IsNullOrEmpty(model.Number) == false) { _CellPhoneNumber = model; HasCellNumber = true; } else { // otherwise the user is deleting it _CellPhoneNumber = new PhoneNumber( ); SetPhoneNumberDigits(""); HasCellNumber = false; } SaveToDevice( ); } if (phoneResult != null) { phoneResult(statusCode, statusDescription); } }); } else { if (phoneResult != null) { phoneResult(System.Net.HttpStatusCode.OK, ""); } } }
void ProcessUrl( ) { // make sure the page is still active. If they browsed away fast enough, // this will fire on the UI thread AFTER the fragment is destroyed. if (IsActive == true && string.IsNullOrEmpty(Url) == false) { // do they want the impersonation token? if (IncludeImpersonationToken) { // try to get it MobileAppApi.TryGetImpersonationToken( delegate(string impersonationToken) { // one more active check, because we fetched the Impersonation Token which // suspended our thread and allowed the OS to potentially tear down this fragment. if (IsActive == true) { // append the mobile platform string fullUrl = Rock.Mobile.Util.Strings.Parsers.AddParamToURL(Url, PrivateGeneralConfig.MobilePlatform); // if we got it, append it and load if (string.IsNullOrEmpty(impersonationToken) == false) { fullUrl += "&" + impersonationToken; } Console.WriteLine("Browsing to {0}", fullUrl); WebLayout.LoadUrl(fullUrl, PrivateGeneralConfig.ExternalUrlToken, PageLoaded); } }); } else { // no impersonation token requested. just load. Console.WriteLine("Browsing to {0}", Url); WebLayout.LoadUrl(Url, PrivateGeneralConfig.ExternalUrlToken, PageLoaded); } } }
public static void GetGroups(int groupTypeId, string street, string city, string state, string zip, int skip, int top, GetGroupsComplete onCompletion) { MobileAppApi.GroupSearchResult sourceLocation = new MobileAppApi.GroupSearchResult( ); // first convert the address into a location object RockApi.Get_Locations_FromAddress(street, city, state, zip, delegate(System.Net.HttpStatusCode statusCode, string statusDescription, Rock.Client.Location model) { // verify the call was successful AND it resulted in a valid location if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true && model != null && model.Latitude.HasValue == true && model.Longitude.HasValue == true) { // take the source location so we can provide it to the caller sourceLocation.Latitude = model.Latitude.Value; sourceLocation.Longitude = model.Longitude.Value; MobileAppApi.GetPublicGroupsByLocation(groupTypeId, model.Id, skip, top, delegate(List <MobileAppApi.GroupSearchResult> searchResults) { if (searchResults != null) { onCompletion(sourceLocation, searchResults, true); } else { // pass on empty list on failure onCompletion(sourceLocation, new List <MobileAppApi.GroupSearchResult>(), false); } }); } else { onCompletion(sourceLocation, new List <MobileAppApi.GroupSearchResult>( ), Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true ? true : false); } }); }
void Internal_DisplayView( ) { // default all controls to hidden, and we'll figure out what to show in the layout method. HideControls(true); IsDownloading = true; BlockerView.Show(delegate { MobileAppApi.GetGroupSummary(GroupEntry.Id, delegate(MobileAppApi.GroupInfo groupInfo, System.IO.MemoryStream familyPhoto, System.IO.MemoryStream groupPhoto) { try { IsDownloading = false; if (familyPhoto != null) { // setup the family image FamilyImage.Image = familyPhoto; float imageSize = Rock.Mobile.Graphics.Util.UnitToPx(PrivateConnectConfig.GroupInfo_Leader_ImageSize); FamilyImage.Bounds = new RectangleF(0, 0, imageSize, imageSize); // if we had a valid image stream, dispose of it now FamilyImageValid = true; familyPhoto.Dispose( ); } else { FamilyImageValid = false; } // setup the group image if (groupPhoto != null) { GroupImage.Image = groupPhoto; float imageSize = Rock.Mobile.Graphics.Util.UnitToPx(PrivateConnectConfig.GroupInfo_Group_ImageSize); GroupImage.Bounds = new RectangleF(0, 0, imageSize, imageSize); GroupImageValid = true; groupPhoto.Dispose( ); } else { // if we had a valid image stream, dispose of it now GroupImageValid = false; } // set the details for the group (distance, meeting time, etc) // set the group title GroupTitle.Text = GroupEntry.Name; if (string.IsNullOrWhiteSpace(GroupEntry.MeetingTime) == false) { MeetingTime.Text = GroupEntry.MeetingTime; } else { MeetingTime.Text = ConnectStrings.GroupFinder_ContactForTime; } // add the distance MeetingTime.Text += "\n" + string.Format("{0:##.0} {1}", GroupEntry.DistanceFromSource, ConnectStrings.GroupFinder_MilesSuffix); // childcare provided header if (string.IsNullOrWhiteSpace(groupInfo.Filters) == false && groupInfo.Filters.Contains(PrivateConnectConfig.GroupFinder_Childcare_Filter)) { ChildcareProvided.Text = ConnectStrings.GroupFinder_OffersChildcare; } else { ChildcareProvided.Text = string.Empty; } // young adults header if (string.IsNullOrWhiteSpace(groupInfo.Filters) == false && groupInfo.Filters.Contains(PrivateConnectConfig.GroupFinder_YoungAdults_Filter)) { YoungAdults.Text = ConnectStrings.GroupFinder_YoungAdults; } else { YoungAdults.Text = string.Empty; } // childcare description (if its blank, it'll be hidden) ChildDesc.Text = groupInfo.ChildcareDesc; // group description (if its blank, it'll be hidden) GroupDesc.Text = groupInfo.Description; BlockerView.Hide( ); GroupSummaryResult(HttpStatusCode.OK, string.Empty); } catch { BlockerView.Hide( ); ResultView.Show(ConnectStrings.GroupInfo_Failed, PrivateControlStylingConfig.Result_Symbol_Failed, ConnectStrings.GroupInfoResult_Failed, GeneralStrings.Retry); GroupSummaryResult(HttpStatusCode.NotFound, string.Empty); } }); }); }
/// <summary> /// Utility function to simplify handling a URL with or without an impersonation token, in an embedded webView OR external browser. /// </summary> public static void HandleUrl(bool launchesExternalBrowser, bool usesImpersonationToken, string url, Task currTask, TaskWebFragment webFragment) { // guard against malformed URLs if (url.StartsWith("http", StringComparison.InvariantCultureIgnoreCase) == true) { // run the url through our processor to see if it needs to be manipulated string processedUrl = Rock.Mobile.Util.URL.Override.ProcessURLOverrides(url); // see if it's external if (processedUrl.StartsWith(PrivateGeneralConfig.ExternalUrlToken, StringComparison.InvariantCultureIgnoreCase)) { // strip off the PrivateGeneralConfig.ExternalUrlToken and forward it processedUrl = processedUrl.Substring(PrivateGeneralConfig.ExternalUrlToken.Length); // and flag that we should launch externally, so that the rest of the function runs as normal launchesExternalBrowser = true; } // are we launching a seperate browser? if (launchesExternalBrowser == true) { // do they also want the impersonation token? if (usesImpersonationToken) { // try to get it MobileAppApi.TryGetImpersonationToken( delegate(string impersonationToken) { // append the mobile platform string fullUrl = Rock.Mobile.Util.Strings.Parsers.AddParamToURL(processedUrl, PrivateGeneralConfig.MobilePlatform); // if we got the token, append it if (string.IsNullOrEmpty(impersonationToken) == false) { fullUrl += "&" + impersonationToken; } // now fire off an intent. Android.Net.Uri uri = Android.Net.Uri.Parse(fullUrl); var intent = new Intent(Intent.ActionView, uri); ((Activity)Rock.Mobile.PlatformSpecific.Android.Core.Context).StartActivity(intent); }); } else { // pretty easy, just fire off an intent. Android.Net.Uri uri = Android.Net.Uri.Parse(processedUrl); var intent = new Intent(Intent.ActionView, uri); ((Activity)Rock.Mobile.PlatformSpecific.Android.Core.Context).StartActivity(intent); } } else { // otherwise we're not, so its simpler webFragment.DisplayUrl(processedUrl, usesImpersonationToken); currTask.PresentFragment(webFragment, true); } } }
/// <summary> /// Handles taking a URL and either launching it externally or within a webView, with or without impersonation token info. (Showing an embedded web page OR launching Safari) /// </summary> public static void HandleUrl(bool launchExternalBrowser, bool includeImpersonationToken, string url, Task parentTask, UIViewController currController, bool disableIdleTimer, bool webViewControlsNavbar, bool webviewHidesNavbarOnScroll, bool animateSeguePresentation = true) { // run the url through our processor to see if it needs to be manipulated string processedUrl = Rock.Mobile.Util.URL.Override.ProcessURLOverrides(url); // see if it's external if (processedUrl.StartsWith(PrivateGeneralConfig.ExternalUrlToken, StringComparison.InvariantCultureIgnoreCase)) { // strip off the PrivateGeneralConfig.ExternalUrlToken and forward it processedUrl = processedUrl.Substring(PrivateGeneralConfig.ExternalUrlToken.Length); // and flag that we should launch externally, so that the rest of the function runs as normal launchExternalBrowser = true; } // do we launch them out of the app? if (launchExternalBrowser == true) { // should we get the impersonation token? if (includeImpersonationToken == true) { MobileAppApi.TryGetImpersonationToken( delegate(string impersonationToken) { // put the platform we're running string fullUrl = Rock.Mobile.Util.Strings.Parsers.AddParamToURL(processedUrl, PrivateGeneralConfig.MobilePlatform); // URL encode the value NSString encodedUrlString = fullUrl.UrlEncode( ); // if we got a token, append it NSUrl encodedUrl = null; if (string.IsNullOrEmpty(impersonationToken) == false) { encodedUrl = new NSUrl(encodedUrlString + "&" + impersonationToken); } else { encodedUrl = new NSUrl(encodedUrlString); } UIApplication.SharedApplication.OpenUrl(encodedUrl); }); } else { // first encode the url NSString encodedUrlString = processedUrl.UrlEncode( ); UIApplication.SharedApplication.OpenUrl(new NSUrl(encodedUrlString)); } } // they are using an embedded browser, so this is pretty simple else { TaskWebViewController viewController = new TaskWebViewController(processedUrl, parentTask, includeImpersonationToken, disableIdleTimer, webViewControlsNavbar, webviewHidesNavbarOnScroll); parentTask.PerformSegue(currController, viewController, animateSeguePresentation); } }
void GetNews(HttpRequest.RequestResult resultCallback) { MobileAppApi.GetNews( delegate(System.Net.HttpStatusCode statusCode, string statusDescription, List <Rock.Client.ContentChannelItem> model) { if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { Rock.Mobile.Util.Debug.WriteLine("Got news from Rock."); // before comitting to this news, make sure there's at least one valid news item. if (model.Count > 0 && model[0].AttributeValues != null) { // sort it by priority model.Sort(delegate(Rock.Client.ContentChannelItem x, Rock.Client.ContentChannelItem y) { return(x.Priority < y.Priority ? -1 : 1); }); // clear existing news Data.News.Clear( ); // parse and take the new items foreach (Rock.Client.ContentChannelItem item in model) { // it's possible rock sent us bad data, so guard against any incomplete news items if (item.AttributeValues != null) { // we do this so we can store it on the stack and print it out if there's an exception. string currKey = ""; try { currKey = "FeatureImage"; string featuredGuid = item.AttributeValues[currKey].Value; string imageUrl = GeneralConfig.RockBaseUrl + "GetImage.ashx?Guid=" + featuredGuid; currKey = "DetailsURL"; string detailUrl = item.AttributeValues[currKey].Value; currKey = "DetailsURLLaunchesBrowser"; bool detailUrlLaunchesBrowser = bool.Parse(item.AttributeValues[currKey].Value); currKey = "IncludeImpersonationToken"; bool includeImpersonationToken = bool.Parse(item.AttributeValues[currKey].Value); currKey = "MobileAppSkipDetailsPage"; bool mobileAppSkipDetailsPage = bool.Parse(item.AttributeValues[currKey].Value); // take a list of the campuses that this news item should display for // (if the list is blank, we'll show it for all campuses) currKey = "Campuses"; List <Guid> campusGuids = new List <Guid>( ); if (item.AttributeValues[currKey] != null && string.IsNullOrEmpty(item.AttributeValues[currKey].Value) == false) { // this will be a comma-dilimited list of campuses to use for the news string[] campusGuidList = item.AttributeValues[currKey].Value.Split(','); foreach (string campusGuid in campusGuidList) { campusGuids.Add(Guid.Parse(campusGuid)); } } // Use the image guids, rather than news title, for the image. // This will ensure the image updates anytime it's changed in Rock! RockNews newsItem = new RockNews(item.Title, item.Content, detailUrl, mobileAppSkipDetailsPage, detailUrlLaunchesBrowser, includeImpersonationToken, imageUrl, featuredGuid.AsLegalFilename( ) + ".bin", campusGuids); // handle developer fields // do a quick check and see if this should be flagged 'private' bool newsPublic = IsNewsPublic(item); newsItem.Developer_Private = !newsPublic; newsItem.Developer_StartTime = item.StartDateTime; newsItem.Developer_EndTime = item.ExpireDateTime; newsItem.Developer_ItemStatus = item.Status; Data.News.Add(newsItem); } catch (Exception e) { // one of the attribute values we wanted wasn't there. Package up what WAS there and report // the error. We can then use process of elimination to fix it. Rock.Mobile.Util.Debug.WriteLine(string.Format("News Item Exception. Attribute Value not found is: {0}. Full Exception {1}", currKey, e)); // Xamarin Insights was able to report on exceptions. HockeyApp cannot as of Mar 2017. // When this functionality becomes available, we should implement it here in just Release Mode. // - CG } } } } } else { Rock.Mobile.Util.Debug.WriteLine("News request failed."); } if (resultCallback != null) { resultCallback(statusCode, statusDescription); } }); }
void GetPECampaign(int?personId, HttpRequest.RequestResult resultCallback) { MobileAppApi.GetPECampaign(personId, delegate(System.Net.HttpStatusCode statusCode, string statusDescription, JArray responseBlob) { if (Rock.Mobile.Network.Util.StatusInSuccessRange(statusCode) == true) { //attempt to parse the response into a single item. If this fails, we'll stop and return nothing. try { JObject campaignBlob = responseBlob.First?.ToObject <JObject>( ); JObject contentBlob = JObject.Parse(campaignBlob["ContentJson"].ToString( )); JObject mobileAppNewsFeedBlob = JObject.Parse(contentBlob[PrivateGeneralConfig.PersonalizationEngine_MobileAppNewsFeed_Key].ToString( )); // get the NAME of the campaign (different than the title displayed) - Note that we get this from CAMPAIGN blob. string campaignName = campaignBlob["Name"]?.ToString( ); // try getting values for each piece of the campaign string title = mobileAppNewsFeedBlob["title"]?.ToString( ); string body = mobileAppNewsFeedBlob["body"]?.ToString( ); string linkUrl = mobileAppNewsFeedBlob["link"]?.ToString( ); string skipDetailsPageStr = mobileAppNewsFeedBlob["skip-details-page"]?.ToString( ); string imgUrl = mobileAppNewsFeedBlob["img"]?.ToString( ); // now parse data as needed bool skipDetailsPage = false; bool.TryParse(skipDetailsPageStr, out skipDetailsPage); // make sure the detail url has a valid scheme / domain, and isn't just a relative url if (linkUrl?.StartsWith("/", StringComparison.CurrentCulture) == true) { linkUrl = GeneralConfig.RockBaseUrl + linkUrl; } // get the url for the image string imageUrl = GeneralConfig.RockBaseUrl + imgUrl; // For the image, we'll cache it using the campaign's title as the filename, plus a version number. // strip the query param for the imageUrl, and use the version argument to control the versioning of the file. // This way, we can cache the image forever, and only update the image when it's actually changed on the server. // Example: https://rock.ccv.church/images/share.jpg?v=0 will save to the device as "share0.bin" // If v=0 becomes v=1, that will then turn into a new filename on the device and cause it to update. Uri imageUri = new Uri(imageUrl); var queryParams = System.Web.HttpUtility.ParseQueryString(imageUri.Query); string imageVersion = queryParams.Get("v") ?? "0"; // build the image filename string imageCacheFileName = (campaignName ?? "campaign-img") + imageVersion + ".bin"; imageCacheFileName = imageCacheFileName.Replace(" ", "").ToLower( ); bool detailUrlLaunchesBrowser = false; bool includeImpersonationToken = true; RockNews newsItem = new RockNews(title, body, linkUrl, skipDetailsPage, detailUrlLaunchesBrowser, includeImpersonationToken, imageUrl, imageCacheFileName, new List <Guid>( )); //support all campuses Data.PECampaign = newsItem; } catch { //something about the response was bad. Rather than crash the entire app, let's just fail here. Rock.Mobile.Util.Debug.WriteLine(statusDescription = string.Format("Getting PE campaigned failed: {0}", statusCode)); statusCode = System.Net.HttpStatusCode.InternalServerError; } } if (resultCallback != null) { resultCallback(statusCode, statusDescription); } }); }