Create and save multiple list of text into Isolated Storage - list

How do I create more than one list of text data and save it into the isolated storage?
I need to retrieve and display different saved list as well.
I am doing an application like a drink list where user can create multiple drink list containing many different kinds of drink.
I can only create and save one list of drink text at the moment. If I were to add more drink text inside the list again and save it, the list will be overwritten by the latest different drink text.
// Save List of drink text
private void addListBtn_Click(object sender, RoutedEventArgs e)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
storage.CreateDirectory("ListFolder");
StreamWriter writeFile = new StreamWriter(new IsolatedStorageFileStream("ListFolder\\savedList.txt", FileMode.OpenOrCreate, storage));
for (int i = 0; i < (Application.Current as App).userDrinksList.Count; i++)
{
String drink = (Application.Current as App).userDrinksList[i].ToString();
writeFile.WriteLine(drink.ToString());
}
writeFile.Close();
MessageBox.Show("List added into favourite list.");
}
// Display saved lists
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
StreamReader readFile = null;
{
readFile = new StreamReader(new IsolatedStorageFileStream("ListFolder\\savedList.txt", FileMode.Open, storage));
listNumberListBox.Items.Add(readFile.ReadToEnd());
readFile.Close();
}
}

You are saving it as savedList.txt. You need to save each list as a separate file. eg list1.txt, list2.txt etc.
Perhaps you also need a list of lists so you know which file = which list.

Your addListBtn_Click method is assuming it can find the list of drinks in a userDrinksList member of your Application instance, however your PhoneApplicationPage_Loaded method doesn't populate that member.
In your PhoneApplicationPage_Loaded method you could do:
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication())
using(var stream = storage.OpenFile("ListFolder\\savedList.txt", FileMode.Open))
using(StreamReader readFile = new StreamReader(stream))
{
for (string line = readFile.ReadLine(); line != null; line = readFile.ReadLine())
{
listNumberListBox.Items.Add(line);
((App) Application.Current).userDrinksList.Add(line)
}
}
The 'usings' ensure that the resources are properly closed/disposed, so you don't need to explicitly close. You were reading in the complete contents - you need to read it in line by line.

Related

Osmdroid 5.6, offline MBTiles

I'm trying to use MBtiles offline with osmdroid,
I took code sample from here https://github.com/osmdroid/osmdroid/blob/master/OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/tileproviders/SampleOfflineOnly.java
But always empty map shown, is there problem with my code?
my code is:
public class OSMDroid extends AppCompatActivity {
private MapView mapView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_osmdroid);
mapView = (MapView) findViewById(R.id.map);
String name = "map.mbtiles";
File f = new File(Environment.getExternalStorageDirectory() + "/osmdroid", name);
if (f.exists()) {
try {
//ok found a file we support and have a driver for the format, for this demo, we'll just use the first one
//create the offline tile provider, it will only do offline file archives
//again using the first file
OfflineTileProvider tileProvider = new OfflineTileProvider(new SimpleRegisterReceiver(this),
new File[]{f});
//tell osmdroid to use that provider instead of the default rig which is (asserts, cache, files/archives, online
mapView.setTileProvider(tileProvider);
//this bit enables us to find out what tiles sources are available. note, that this action may take some time to run
//and should be ran asynchronously. we've put it inline for simplicity
String source = "";
IArchiveFile[] archives = tileProvider.getArchives();
if (archives.length > 0) {
//cheating a bit here, get the first archive file and ask for the tile sources names it contains
Set<String> tileSources = archives[0].getTileSources();
//presumably, this would be a great place to tell your users which tiles sources are available
if (!tileSources.isEmpty()) {
//ok good, we found at least one tile source, create a basic file based tile source using that name
//and set it. If we don't set it, osmdroid will attempt to use the default source, which is "MAPNIK",
//which probably won't match your offline tile source, unless it's MAPNIK
source = tileSources.iterator().next();
mapView.setTileSource(FileBasedTileSource.getSource(source));
} else {
mapView.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
}
} else {
mapView.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
}
mapView.setUseDataConnection(false);
mapView.setBuiltInZoomControls(true);
IMapController mapController = mapView.getController();
mapController.setZoom(10);
GeoPoint startPt = new GeoPoint(61.5797,51.5997);
mapController.setCenter(startPt);
mapView.invalidate();
return;
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
MBTilesFileArchive getTileSources always returns empty string, here is implementation:
public Set<String> getTileSources(){
//the MBTiles spec doesn't store source information in it, so we can't return anything
return Collections.EMPTY_SET;
}
In order to make a offline map you should add tiles first. You can use Maperitive app to make your map tiles(zip is easier to manage than sql). Name the zip MapquestOSM. After you have done it create a folder "osmdroid" in phones memory(Directly into the internal memory or sd card) and add your map tiles in it.
Parameters of the XYTileSource changes related to the map tiles you have created. This code handles everything about map tiles itself. I hope it helps you
mapView.setUseDataConnection(false);
mapView.setTileSource(new XYTileSource("MapquestOSM", 2, 15, 256, ".png", new String[]{}));

Serializing/parsing multiple objects in one file in Qt C++

I need to serialize and parse multiple objects from my project, in order to save/load them when needed.
My objects will have exactly the same components : a QString name, an integer id, a QString description, and two integer x, y.
I'll need something like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
So I'll build my QJsonObject like this :
QJsonObject json;
json["id"] = object_to_serialize.get_id();
json["name"] = object_to_serialize.get_name();
json["description"] = object_to_serialize.get_description();
json["x"] = object_to_serialize.get_x();
json["y"] = object_to_serialize.get_y();
QJsonDocument filedoc(json);
file.write(filedoc.toJson);`
And in the file it will appear like this :
{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
My serialiser will take in parameter the object, the savefile name, and transform the object into a QJsonObject. It will need then to read the file to check if an object with the same id is here. If it is here, it will need to replace it, and if it is not, it will append it.
I'm a little lost between my serialization options and how to read it ;
Should I make a QJsonArray with multiple QJsonObject inside or QJsonObject with QJsonArrays ?
When I read it, I will need to check for the id ; but will a
foreach(object.value["id"] == 42)
//create the QJsonObject from the one with 42 and change it with the new data
will do to parse the object and not all of them ? Is there a better way ?
Thank you in advance for your answers.
You can have an array of json object, each of them having an ID so you can parse the relevant ones.
Although you could also parse all of them and add them in a map, as long as you don't have very heavy files it should be fine.
void parseJson(const QString &data)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (doc.isNull())
{
war("invalid json document");
return;
}
QJsonArray jsonArray = doc.array();
foreach (const QJsonValue & value, jsonArray) {
QJsonObject obj = value.toObject();
if (obj.contains("id"))
{
if (obj["id"].toInt() == yourId) parseObject(obj);
}
}
}
void parseObject(const QJsonObject &obj)
{
if (obj.contains("valueA")) valueA = obj["valueA"].toDouble();
if (obj.contains("valueB")) valueB = obj["valueB"].toDouble();
}
This will work just fine if your file is not too big
Bigger Files
Now if you have very large file, it might be an issue to load it all in memory and parse it.
Since your structure is always the same and quite simple, JSON might not be the best choice, one more efficient method would be to do your own parser (or use probably some existing ones) that could read the file and process it as a stream.
Another method, would be to have one JSON entry per line preceded by an ID with a fixed number of digit. Load this in a QHash lookup and then only read id of interest from the file and only parse a small section.
// This code is not tested and is just to show the principle.
#define IDSIZE 5
QHash<int64, int64> m_lookup; // has to be global var
// For very large file, this might take some time and can be done on a separate thread.
// it needs to be done only once at startup (given the file is not modified externally)
void createLookup(const QString &fileName)
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while (!in.atEnd())
{
int position = in.pos(); // store the position in the file
QString line = in.readLine();
int id = line.mid(0,IDSIZE).toInt(); // 5 digit id (like 00001, 00002, etc...
m_lookup[id] = position + IDSIZE;
}
inputFile.close();
}
}
QString getEntry(const QString &fileName, int64 id)
{
if (m_lookup.contains(id))
{
QFile inputFile(fileName);
if (inputFile.open(QIODevice::ReadOnly))
{
inputFile.seek(m_lookup[id]);
QString data = inputFile.readLine();
inputFile.close();
return data;
} else {
return QString(); // or handle error
}
} else {
return QString(); // or handle error
}
}
// use example
QString data = getEntry(id);
if (data.length() > 0)
{
QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
if (!doc.isNull())
{
// assign your variables
}
}
and your data file looking like this:
00042{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
00044{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}
00046{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}
The advantage of this method, it will only read the entry of interest, and avoid having to load MB or GB of data in memory just to get a specific entry.
This could further be improved with a lookup table stored at the beginning of the file.

Sitecore dictionary items Editable via page editor

I am trying to implement Sitecore Dictionary items to be edited via PageEditor.
This is my approach.. Just need your thoughts and suggestions.
To make it simple and not to mess up with the pipelines, here is a simple way of what I am doing.
Normally you do a sitecore translate for example,
#Sitecore.Globalization.Translate.Text("SomeKey")
You can encapsulate the Translate to a custom class which might look like
public static class CustomTranslate
{
public static string Text(string key)
{
if (Sitecore.Context.PageMode.IsPageEditorEditing)
{
string val = String.Empty;
Item currentItem = Context.Database.GetItem(ResourcesController.ItemLookUp()[key]);
if (currentItem != null)
{
val = FieldRenderer.Render(currentItem, "Phrase");
}
return val;
}
else
return Sitecore.Globalization.Translate.Text(key);
}
}
The CustomTranslate.Text returns a FieldRenderer in PageEdit mode else returns the Sitecore.Globalization.Translate.Text
Then in your code you can refer the translations as
#CustomTranslate.Text("SomeKey")
The Lookup can be a dictionary of Key and Item ID as shown in below code,
public static class ResourceController
{
public static Dictionary ItemLookUp()
{
///get dictionary path..etc.. code not included
//read all sitecore dictionary Items
Sitecore.Data.Items.Item[] items =
Sitecore.Context.Database.SelectItems("fast:" + dictionaryPath +
"//*[##templateid='{6D1CD897-1936-4A3A-A511-289A94C2A7B1}']");
//build a Dictionary<string,string> using sitecore item key and Guid.
items.All(y => { resourceDictionary.Add(y["Key"], y.ID.Guid.ToString()); return true;}
// Key,Guid dictionary
return resourceDictionary;
}
}
A Simpler and much easier approach ! Thoughts, Comments ?

Google Glass Mirror API - Is there a way to delete a bundle of cards?

I'm sending a bundle of cards to Glass with the Mirror API (c# library)
I know that you can use the default delete menu item on single cards, but is there a way to provide delete functionality for an entire bundle, ideally the result of one action by the users?
I have successfully used the DELETE action on a menu item with the code below
MenuItem mi = new MenuItem();
mi.Action = "DELETE";
TimelineItem tli = new TimelineItem()
{
Html = itemHtml.ToString(),
Notification = new NotificationConfig() { Level = "DEFAULT" },
MenuItems = new List<MenuItem>() { mi }
};
Is there a way to add this delete menu item to a bundle cover? I know this may be tricky because clicking the bundle cover causes you to navigate into the child cards thus no menu is present like on single cards. I'm looking for something (which I did try but it just ignored the menu item) like this:
MenuItem mi = new MenuItem();
mi.Action = "DELETE";
TimelineItem tli = new TimelineItem()
{
Html = itemHtml.ToString(),
Notification = new NotificationConfig() { Level = "DEFAULT" },
IsBundleCover = true,
BundleId = bundleId,
MenuItems = new List<MenuItem>() { mi }
};
If not possible on a cover card, is there a way to do this for a bundle by adding delete menu items to the child cards?
Any suggestions would be appreciated
You can use customized menu to do this. The code below is using Java but C# should be similar:
Add customized menu item to the card:
List<MenuValue> menuValueList = new ArrayList<MenuValue>();
menuValueList.add(new MenuValue().setIconUrl(iconUrl).setDisplayName("Delete All"));
MenuItem menuItem = new MenuItem();
menuItem.setValues(menuValueList).setId("delete_bundle_A").setAction("CUSTOM");
List<MenuItem> menuItemList = new ArrayList<MenuItem>();
menuItemList.add(menuItem);
timelineItem.setMenuItems(menuItemList);
Define the controller which handles the callback request of Mirror server notification:
if (notification.getCollection().equals("timeline") && notification.getUserActions().contains(new UserAction().setType("CUSTOM").setPayload("delete_bundle_A"))) {
deleteCards(credential, bundleId);
}
The delete card function:
// if bundleId is null or "", delete all cards
public static void deleteCards(Credential credential, String bundleId) throws IOException {
if (bundleId == null) {
bundleId = "";
}
Mirror.Timeline timelineItems = MirrorClient.getMirror(credential).timeline();
Mirror.Timeline.List list = timelineItems.list();
List<TimelineItem> timelineItemList = null;
do {
TimelineListResponse response = list.execute();
timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
if (bundleId == "" || bundleId.equalsIgnoreCase(item.getBundleId())) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
}
list.setPageToken(response.getNextPageToken());
} else {
break;
}
} while (list.getPageToken() != null && list.getPageToken().length() > 0);
}
Finally, don't forget to subscribe timeline notification when application starts up:
String notifyUrl = "https://mirrornotifications.appspot.com/forward?url=" + "http://yourServer.com/notify";
Subscription subscription = MirrorClient.insertSubscription(credential, notifyUrl, userId, "timeline");
It isn't clear if you're asking how to create the menu items to delete the entire bundle at once, or if you're looking for code to do the actual delete.
Yuan provides some very good answers to both (not least of which because he actually provides code, which I won't), but there are three things you might also want to consider.
1) You can't have a menu on the bundle cover, but if you don't explicitly specify a bundle cover, then the most recent card will be shown as the cover and will also be shown as the first card in the bundle. You'd be able to get to the menu this way. (The default messaging app works this way, for example, but the first card has the same menu as the rest.)
2) You don't need to create a new menu item. You can leverage the DELETE menu item, if you wish. You'll get a delete notification for one of the cards in the bundle and you can then read the bundleId and delete the rest.
3) You don't need to loop through all the cards you've inserted just to find ones that have that bundleId. That is horribly inefficient. I am not fluent in C#, but from reading the documentation at https://developers.google.com/resources/api-libraries/documentation/mirror/v1/csharp/latest/classGoogle_1_1Apis_1_1Mirror_1_1v1_1_1TimelineResource_1_1ListRequest.html, I get the sense that you can create a ListRequest and then set the bundleId before executing the query and get the results.
So I think you can change Yuan's code to something like:
Mirror.Timeline.List list = timelineItems.list();
list.BundleId = bundleId;
List<TimelineItem> timelineItemList = null;
do {
TimelineListResponse response = list.execute();
timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
list.setPageToken(response.getNextPageToken());
} else {
break;
}
} while (list.getPageToken() != null && list.getPageToken().length() > 0);
(this should be treated as pseudo-code, at best)
If you're confident how many items you've put into a bundle, you might also be able to just set list.MaxResults and not have to iterate over the pages of results. So perhaps something more like
Mirror.Timeline.List list = timelineItems.list();
list.BundleId = bundleId;
list.MaxResults = 20; // Set to more than the max number of items in a bundle
TimelineListResponse response = list.execute();
List<TimelineItem> timelineItemList = response.getItems();
if (timelineItemList != null && timelineItemList.size() > 0) {
for (TimelineItem item : timelineItemList) {
LOG.info("Deleting card " + item.getId());
MirrorClient.deleteTimelineItem(credential, item.getId());
}
}
There doesn't appear to be a way to delete a bundle in one step but it's still possible...
You can do a GET on /Timeline to get a list of items your app has pushed to the users timeline. Filter that out to find the entries with the bundleId you want to delete. For each of those items, call DELETE /Timeline/{itemid}

Qt C++ Get data from a selected row of a table view

I have a table view populated with data of Vehicle objects stored in a QList<Vehicle> cars; On this cars list i have to make some actions: search, add, edit, delete; After every action of search, i store the founded objects in another list to populate the table view only with this objects, keeping the original list intact. The original list gets modified only on add, edit or delete. Here comes the problem: i search for an object that i want to edit, it is shown in the table view, i select it and press the edit button; i am using QModelIndexList to get the index of the row. The index of the row will give me the position from the "founded" list, and i need to modify the object in the original list. Below is my implementation.
Is there any other way to this thing without the temporary list? How can i modify the original object using Iterator, instead that for loop?
void MainWindow::on_actionEdit_triggered()
{
QMessageBox msgBox;
QModelIndexList id = ui->tableView->selectionModel()->selectedIndexes();
if(id.isEmpty()){
msgBox.setWindowTitle("Message");
msgBox.setText("Please select a row");
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
} else{
int row = id.at(0).row();
QUuid carId = temp.at(row).getVehicleID(); // temp -> the "the founded" list
for(int i = 0; i < cars.size(); i++){
Vehicle& current = cars[i];
Vehicle& currentTemp = temp[row];
if(carId == current.getVehicleID() && carId == currentTemp.getVehicleID()){
addDialog = new AddEditDialog(this);
addDialog->loadVehicleToEdit(current);
addDialog->exec();
if(addDialog->getIsEdited()){
current = addDialog->getVehicleToAdd();
currentTemp = addDialog->getVehicleToAdd();
currentTemp.setVehicleId(carId);
current.setVehicleId(carId);
}
}
}
}
//create header
createHeader(model);
//set data to the table view
populate(temp);
}
May be you use proxy model and you need to call mapToSource?