Skip to content

[BUG] Photos lost on iPhone after app upgrade (Fails to create directory for Storage) #2486

@jsfan3

Description

@jsfan3

I have an app that saves all its data in a TreeSet of PropertyBusinessObject. This data is saved to the Storage and it's restored from the Storage in the init(). Code:

public class StartPoint {

    private Form current;
    public static Resources theme;

    public static TreeMap<Long, ItemProperty> itemsMap = new TreeMap<>();

    public void init(Object context) {
        [...]
        Util.register("ItemProperty", ItemProperty.class);

        TreeMap restoredData = restoreData();
        if (restoredData != null) {
            itemsMap = restoredData;
            Log.p("Data restored");
        }
    }

    public static void saveData() {
        Storage.getInstance().writeObject("MyDiary", itemsMap);
        Log.p("Data saved");
    }

    public TreeMap restoreData() {
        HashMap map = (HashMap) Storage.getInstance().readObject("MyDiary");
        if (map != null) {
            return new TreeMap(map);
        } else {
            return null;
        }
    }
    
public class ItemProperty implements PropertyBusinessObject {

    public final Property<String, ItemProperty> photo = new Property<>("photo");
    public final LongProperty<ItemProperty> dateAndTime = new LongProperty<>("dateAndTime");
    public final Property<String, ItemProperty> quantity = new Property<>("quantity");
    public final Property<String, ItemProperty> place = new Property<>("place");
    public final Property<String, ItemProperty> feeling = new Property<>("feeling");
    public final Property<String, ItemProperty> fullness = new Property<>("fullness");
    public final Property<String, ItemProperty> afterEating = new Property<>("afterEating");
    public final Property<String, ItemProperty> other = new Property<>("other");

    private final PropertyIndex idx = new PropertyIndex(this, "ItemProperty",
            photo, dateAndTime, quantity, place, feeling, fullness,
            afterEating, other);

    @Override
    public PropertyIndex getPropertyIndex() {
        return idx;
    }

    public ItemProperty() {
        this.getPropertyIndex().registerExternalizable();
        Date currentTime = new Date();
        dateAndTime.set(currentTime.getTime());
    }

    public String getLocalizedDate() {
        Date date = new Date(dateAndTime.get());
        return L10NManager.getInstance().formatDateLongStyle(date);
    }
    
    public String getLocalizedDateTime() {
        Date date = new Date(dateAndTime.get());
        return L10NManager.getInstance().formatDateTime(date);
    }
    

    public String getTime() {
        Date date = new Date(dateAndTime.get());
        Calendar myCalendar = Calendar.getInstance();
        myCalendar.setTime(date);
        String hour = Integer.toString(myCalendar.get(Calendar.HOUR_OF_DAY));
        String minute = Integer.toString(myCalendar.get(Calendar.MINUTE));
        if (minute.length() == 1) {
            minute = "0" + minute;
        }
        return hour+":"+minute;
    }

}

I have an iPhone specific problem (that doesn't happen on Android). When I update the app (installing a new build version), the photo of each ItemProperty is lost, while all the other data is correctly restored. Each photo was taken from the camera and then saved to the storage in this way:

// START OF IMAGE SECTION
        Style s = new Style();
        s.setFgColor(ColorUtil.GRAY);
        s.setBgColor(ColorUtil.WHITE);
        Label avatar = new Label(FontImage.createMaterial(FontImage.MATERIAL_ADD_A_PHOTO, s, imgSize / 3));
        avatar.getUnselectedStyle().setPadding(0, 0, 0, 0);
        avatar.getUnselectedStyle().setMargin(0, 0, 0, 0);

        if (data.photo.get() != null && !data.photo.get().isEmpty()) {
            try {
                Image img = Image.createImage(FileSystemStorage.getInstance().openInputStream(data.photo.get()));
                avatar.setIcon(img);
            } catch (IOException ex) {
                Log.p("ERROR: Cannot load the photo");
                Log.e(ex);
            }
        }

        ActionListener callback = e -> {
            Log.p("ActionListener callback invoked");
            if (e != null && e.getSource() != null) {
                String filePath = (String) e.getSource();

                // Resizes and Saves the image
                String pathAvatar = FileSystemStorage.getInstance().getAppHomePath() + System.currentTimeMillis() + ".jpg";
                try {
                    // About image orientation:
                    // https://stackoverflow.com/questions/51480978/codenameone-capture-photo-upside-down
                    OutputStream os2 = FileSystemStorage.getInstance().openOutputStream(pathAvatar);
                    Image img = Image.createImage(FileSystemStorage.getInstance().openInputStream(filePath));
                    if (img.getWidth() >= img.getHeight()) {
                        img = img.scaledWidth(Display.getInstance().convertToPixels(imgSize));
                    } else {
                        img = img.scaledHeight(Display.getInstance().convertToPixels(imgSize));
                    }
                    ImageIO.getImageIO().save(img, os2, ImageIO.FORMAT_JPEG, 0.9f);
                    os2.close();
                    // to open: Image img = Image.createImage(FileSystemStorage.getInstance().openInputStream(pathToImage));
                    data.photo.set(pathAvatar);
                    avatar.setIcon(img);
                    revalidate();
                } catch (IOException ex) {
                    Log.p("ERROR: Cannot save the photo");
                    Log.e(ex);
                }

            }
        };

        Style colorIcons = new Style();
        colorIcons.setFgColor(ColorUtil.LTGRAY);

        Button camera = new Button("Photocamera", FontImage.createMaterial(FontImage.MATERIAL_CAMERA, colorIcons, 5));
        camera.addActionListener((e) -> {
            Capture.capturePhoto(callback);
        });

        Label photo = new Label("Photo");
        photo.setAutoSizeMode(true);
        add(FlowLayout.encloseCenter(photo));
        Container imageAndButton = BoxLayout.encloseY(FlowLayout.encloseCenter(avatar), FlowLayout.encloseCenter(camera));
        imageAndButton.setLeadComponent(camera);
        add(imageAndButton);
        // END OF IMAGE SECTION

The iPhone log doesn't contain any interesting data to understand this issue, except this:

[EDT] 0:2:57,28 - 2018-07-27 11:42:20.177 StartPoint[354:19370] [LOG] 0:0:0,0 - Codename One revisions: c67824ee4d9359c0891df93ad3d03e6c349b5a3f
2018-07-27 11:42:20.181 StartPoint[354:19370] Failed to create directory /var/mobile/Containers/Data/Application/D0F5692E-2B39-4333-8AF1-32B8F83A291E/Documents/cn1storage/

I logged also the data saved before and after the app update. They are identical, they are in this form:
json

The strange fact is that "ERROR: Cannot load the photo" is not logged (see the code above) and the photos taken before the app upgrade are replaced by the placeholder. All the new photo are correctly saved and shown (even if I kill and restard the app)... until the next app upgrade, after that they are lost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions