~aleteoryx/muditaos

71925c63384b9020cd0c077eab2b9c4c5fead472 — pawpMudita 5 years ago 8618ea5
[EGD-4740] Add New/Edit APN window - Part1

Add New/Edit APN window - basic window/ model/ widget
M changelog.md => changelog.md +1 -0
@@ 6,6 6,7 @@

* Add hardware in the loop tests.
* Add empty APN settings window.
* Add New/Edit APN window

### Changed


M image/assets/lang/English.json => image/assets/lang/English.json +8 -0
@@ 363,6 363,14 @@
  "app_settings_security_wrong_passcode": "Wrong passcode!",
  "app_settings_security_passcode_changed_successfully": "Passcode changed successfully!",
  "app_settings_security_passcode_disabled": "Passcode disabled!",
  "app_settings_new_edit_apn": "New/Edit APN",
  "app_settings_apn_name": "Name",
  "app_settings_apn_APN": "APN",
  "app_settings_apn_username": "UserName",
  "app_settings_apn_password": "Password",
  "app_settings_apn_authtype": "Authentication type",
  "app_settings_apn_apntype": "APN Type",
  "app_settings_apn_apnprotocol" : "APN Protocol",
  "app_phonebook_title_main": "Contacts",
  "app_phonebook_search_win_contacts": "Contacts",
  "common_search_uc": "Search",

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +13 -0
@@ 28,6 28,7 @@
#include "windows/QuotesOptionsWindow.hpp"
#include "windows/ChangePasscodeWindow.hpp"
#include "windows/SystemMainWindow.hpp"
#include "windows/NewApnWindow.hpp"

#include "Dialog.hpp"



@@ 233,9 234,21 @@ namespace app
        windowsFactory.attach(gui::window::name::security, [](Application *app, const std::string &name) {
            return std::make_unique<gui::SecurityMainWindow>(app);
        });

        windowsFactory.attach(gui::window::name::system, [](Application *app, const std::string &name) {
            return std::make_unique<gui::SystemMainWindow>(app);
        });

        windowsFactory.attach(gui::window::name::change_passcode, [](Application *app, const std::string &name) {
            return std::make_unique<gui::ChangePasscodeWindow>(app);
        });
        windowsFactory.attach(gui::window::name::dialog_confirm, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogConfirm>(app, gui::window::name::dialog_confirm);
        });

        windowsFactory.attach(gui::window::name::new_apn, [](Application *app, const std::string &name) {
            return std::make_unique<gui::NewApnWindow>(app);
        });
    }

    void ApplicationSettingsNew::destroyUserInterface()

M module-apps/application-settings-new/ApplicationSettings.hpp => module-apps/application-settings-new/ApplicationSettings.hpp +2 -0
@@ 55,6 55,8 @@ namespace gui::window::name
    inline constexpr auto about_your_pure = "AboutYourPure";
    inline constexpr auto certification   = "Certification";

    inline constexpr auto new_apn = "NewApn";

} // namespace gui::window::name

namespace app

M module-apps/application-settings-new/CMakeLists.txt => module-apps/application-settings-new/CMakeLists.txt +7 -0
@@ 18,6 18,7 @@ target_sources( ${PROJECT_NAME}
        widgets/timeWidget.cpp
        widgets/ChangePasscodeLockHandler.cpp
        widgets/QuoteWidget.cpp
        widgets/ApnInputWidget.cpp
        windows/SettingsMainWindow.cpp
        windows/AddDeviceWindow.cpp
        windows/AllDevicesWindow.cpp


@@ 42,12 43,17 @@ target_sources( ${PROJECT_NAME}
        windows/QuotesAddWindow.cpp
        windows/SecurityMainWindow.cpp
        windows/ChangePasscodeWindow.cpp
        windows/NewApnWindow.cpp
        models/NewApnModel.cpp
        widgets/SpinBox.cpp
        widgets/SpinBoxOptionSetting.cpp
        windows/SystemMainWindow.cpp

    PUBLIC
        ApplicationSettings.hpp
        widgets/ChangePasscodeLockHandler.hpp
        widgets/ApnInputWidget.hpp
        windows/NewApnWindow.hpp
        windows/SettingsMainWindow.hpp
        windows/BaseSettingsWindow.hpp
        windows/FontSizeWindow.hpp


@@ 62,6 68,7 @@ target_sources( ${PROJECT_NAME}
        windows/AutolockWindow.hpp
        windows/WallpaperWindow.hpp
        windows/SystemMainWindow.hpp
        windows/ChangePasscodeWindow.hpp
)

add_dependencies(${PROJECT_NAME} version)

A module-apps/application-settings-new/data/SettingsInternals.hpp => module-apps/application-settings-new/data/SettingsInternals.hpp +30 -0
@@ 0,0 1,30 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>

namespace settingsInternals
{
    enum class ListItemName
    {
        Name,
        APN,
        Proxy,
        Port,
        Username,
        Password,

        Server,
        MMSC,
        MmsProxy,
        MmsPort,
        MCC,
        MNC,
        AuthType,
        ApnType,
        ApnProtocol
    };

} // namespace settingsInternals

A module-apps/application-settings-new/data/SettingsItemData.hpp => module-apps/application-settings-new/data/SettingsItemData.hpp +23 -0
@@ 0,0 1,23 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <service-cellular/service-cellular/PacketDataTypes.hpp>
#include <ListItem.hpp>
#include <SwitchData.hpp>

class ApnItemData : public gui::SwitchData
{
  public:
    ApnItemData(std::shared_ptr<packet_data::APN::Config> Apn) : apn(std::move(Apn)){};
    ApnItemData() : apn(nullptr){};

    auto getApn() -> std::shared_ptr<packet_data::APN::Config>
    {
        return apn;
    }

  private:
    std::shared_ptr<packet_data::APN::Config> apn;
};

A module-apps/application-settings-new/models/NewApnModel.cpp => module-apps/application-settings-new/models/NewApnModel.cpp +140 -0
@@ 0,0 1,140 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewApnModel.hpp"

#include "AppWindow.hpp"
#include "application-settings-new/widgets/ApnInputWidget.hpp"
#include <service-cellular/service-cellular/CellularServiceAPI.hpp>

#include <ListView.hpp>
#include <time/ScopedTime.hpp>
#include <BottomBar.hpp>

NewApnModel::NewApnModel(app::Application *app) : application(app)
{}

auto NewApnModel::requestRecordsCount() -> unsigned int
{
    return internalData.size();
}

auto NewApnModel::getMinimalItemHeight() const -> unsigned int
{
    return style::settings::widget::apnInputWidget::h;
}

void NewApnModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

auto NewApnModel::getItem(gui::Order order) -> gui::ListItem *
{
    return getRecord(order);
}

void NewApnModel::createData()
{
    auto app = application;

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::Name,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::APN,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::Username,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::Password,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::AuthType,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::ApnType,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    internalData.emplace_back(new gui::ApnInputWidget(
        settingsInternals::ListItemName::ApnProtocol,
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); },
        [this]() { this->apnDataChanged(); }));

    for (auto item : internalData) {
        item->deleteByList = false;
    }
}

void NewApnModel::clearData()
{
    list->clear();

    eraseInternalData();

    createData();

    list->rebuildList();
}

void NewApnModel::saveData(std::shared_ptr<packet_data::APN::Config> apnRecord)
{
    for (auto item : internalData) {
        if (item->onSaveCallback) {
            item->onSaveCallback(apnRecord);
        }
    }
}

void NewApnModel::loadData(std::shared_ptr<packet_data::APN::Config> apnRecord)
{
    for (auto item : internalData) {
        if (item->onLoadCallback) {
            item->onLoadCallback(apnRecord);
        }
    }
}

void NewApnModel::apnDataChanged()
{
    for (auto item : internalData) {
        if (item->onEmptyCallback && !item->onEmptyCallback()) {
            application->getCurrentWindow()->setBottomBarActive(gui::BottomBar::Side::CENTER, true); // SAVE button
            return;
        }
    }
    application->getCurrentWindow()->setBottomBarActive(gui::BottomBar::Side::CENTER, false); // SAVE button
    return;
}

void NewApnModel::apnSendRecord(packet_data::APN::Config apnRecord)
{
    CellularServiceAPI::SetAPN(application, apnRecord);
}

A module-apps/application-settings-new/models/NewApnModel.hpp => module-apps/application-settings-new/models/NewApnModel.hpp +36 -0
@@ 0,0 1,36 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-settings-new/data/SettingsItemData.hpp"
#include "application-settings-new/widgets/ApnListItem.hpp"
#include "application-settings-new/widgets/SettingsStyle.hpp"
#include "InternalModel.hpp"
#include "Application.hpp"

#include <ListItemProvider.hpp>

class NewApnModel : public app::InternalModel<gui::ApnListItem *>, public gui::ListItemProvider
{
    app::Application *application = nullptr;

  public:
    NewApnModel(app::Application *app);

    void clearData();
    void saveData(std::shared_ptr<packet_data::APN::Config> apnRecord);
    void loadData(std::shared_ptr<packet_data::APN::Config> apnRecord);

    void createData();

    [[nodiscard]] auto requestRecordsCount() -> unsigned int override;

    [[nodiscard]] auto getMinimalItemHeight() const -> unsigned int override;

    auto getItem(gui::Order order) -> gui::ListItem * override;

    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    void apnDataChanged();
    void apnSendRecord(packet_data::APN::Config apnRecord);
};

A module-apps/application-settings-new/widgets/ApnInputWidget.cpp => module-apps/application-settings-new/widgets/ApnInputWidget.cpp +221 -0
@@ 0,0 1,221 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApnInputWidget.hpp"
#include <Span.hpp>
#include "application-settings-new/widgets/SettingsStyle.hpp"
#include <i18n/i18n.hpp>
#include <utility>

namespace gui
{
    ApnInputWidget::ApnInputWidget(settingsInternals::ListItemName listItemName,
                                   std::function<void(const UTF8 &)> bottomBarTemporaryMode,
                                   std::function<void()> bottomBarRestoreFromTemporaryMode,
                                   std::function<void()> selectSpecialCharacter,
                                   std::function<void()> contentChanged,
                                   unsigned int lines)
        : listItemName(listItemName), checkTextContent(std::move(contentChanged))
    {

        setMinimumSize(style::settings::widget::apnInputWidget::w,
                       style::settings::widget::apnInputWidget::title_label_h +
                           style::settings::widget::apnInputWidget::span_size +
                           style::settings::widget::apnInputWidget::input_text_h * lines);

        setMargins(gui::Margins(0, style::margins::huge, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        titleLabel = new Label(vBox);
        titleLabel->setMinimumSize(style::settings::widget::apnInputWidget::w,
                                   style::settings::widget::apnInputWidget::title_label_h);
        titleLabel->setEdges(RectangleEdge::None);
        titleLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        titleLabel->setFont(style::window::font::verysmall);
        titleLabel->activeItem = false;

        inputText = new TextFixedSize(vBox, 0, 0, 0, 0);
        inputText->setMinimumSize(style::settings::widget::apnInputWidget::w,
                                  style::settings::widget::apnInputWidget::input_text_h * lines);
        inputText->setMargins(Margins(0, style::settings::widget::apnInputWidget::span_size, 0, 0));
        inputText->setUnderlinePadding(style::settings::widget::apnInputWidget::underline_padding);

        inputText->setEdges(RectangleEdge::None);
        inputText->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        inputText->setFont(style::window::font::medium);
        inputText->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { bottomBarTemporaryMode(text); },
            [=]() { bottomBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));
        inputText->setPenFocusWidth(style::window::default_border_focus_w);
        inputText->setPenWidth(style::window::default_border_no_focus_w);
        inputText->setEditMode(EditMode::Edit);

        applyItemNameSpecificSettings();

        focusChangedCallback = [&](Item &item) {
            setFocusItem(focus ? vBox : nullptr);

            auto tempText = inputText->getText();

            if (focus) {
                inputText->setFont(style::window::font::mediumbold);
                inputText->setText(tempText);
            }
            else {
                inputText->setFont(style::window::font::medium);
                inputText->setText(tempText);
            }
            return true;
        };

        inputCallback = [&](Item &item, const InputEvent &event) {
            auto result = inputText->onInput(event);
            if (checkTextContent != nullptr) {
                checkTextContent();
            }
            return result;
        };
        setEdges(RectangleEdge::None);
    }

    auto ApnInputWidget::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool
    {
        vBox->setPosition(0, 0);
        vBox->setSize(newDim.w, newDim.h);

        return true;
    }

    void ApnInputWidget::applyItemNameSpecificSettings()
    {
        switch (listItemName) {

        case settingsInternals::ListItemName::Name:
            nameHandler();
            break;

        case settingsInternals::ListItemName::APN:
            apnHandler();
            break;

        case settingsInternals::ListItemName::Username:
            usernameHandler();
            break;

        case settingsInternals::ListItemName::Password:
            passwordNumberHandler();
            break;

        case settingsInternals::ListItemName::AuthType:
            authtypeHandler();
            break;

        case settingsInternals::ListItemName::ApnType:
            apntypeHandler();
            break;

        case settingsInternals::ListItemName::ApnProtocol:
            protocolHandler();
            break;

        default:
            LOG_ERROR("Incorrect List Item Name!");
            break;
        }
    }

    void ApnInputWidget::nameHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_name"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->apn = inputText->getText();
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->apn);
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

    void ApnInputWidget::apnHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_APN"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->ip = inputText->getText();
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->ip);
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

    void ApnInputWidget::usernameHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_username"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->username = inputText->getText();
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->username);
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

    void ApnInputWidget::passwordNumberHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_password"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->password = inputText->getText();
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->password);
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

    void ApnInputWidget::authtypeHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_authtype"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->setAuthMethod(inputText->getText());
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->getAuthMethod());
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

    void ApnInputWidget::apntypeHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_apntype"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->setApnType(inputText->getText());
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->getApnType());
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }
    void ApnInputWidget::protocolHandler()
    {
        titleLabel->setText(utils::localize.get("app_settings_apn_apnprotocol"));
        inputText->setTextType(TextType::SingleLine);
        onSaveCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            apnRecord->setApnProtocol(inputText->getText());
        };
        onLoadCallback = [&](std::shared_ptr<packet_data::APN::Config> apnRecord) {
            inputText->setText(apnRecord->getApnProtocol());
        };
        onEmptyCallback = [&]() { return inputText->isEmpty(); };
    }

} /* namespace gui */

A module-apps/application-settings-new/widgets/ApnInputWidget.hpp => module-apps/application-settings-new/widgets/ApnInputWidget.hpp +44 -0
@@ 0,0 1,44 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-settings-new/data/SettingsInternals.hpp"
#include "application-settings-new//widgets/ApnListItem.hpp"

#include <ListItem.hpp>
#include <Text.hpp>
#include <TextFixedSize.hpp>

namespace gui
{
    class ApnInputWidget : public ApnListItem
    {
        settingsInternals::ListItemName listItemName;

      public:
        ApnInputWidget(settingsInternals::ListItemName listItemName,
                       std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                       std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr,
                       std::function<void()> selectSpecialCharacter                 = nullptr,
                       std::function<void()> contentChanged                         = nullptr,
                       unsigned int lines                                           = 1);

      private:
        VBox *vBox                             = nullptr;
        Label *titleLabel                      = nullptr;
        TextFixedSize *inputText               = nullptr;
        std::function<void()> checkTextContent = nullptr;

        void applyItemNameSpecificSettings();
        auto onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim) -> bool override;
        void nameHandler();
        void apnHandler();
        void usernameHandler();
        void passwordNumberHandler();
        void authtypeHandler();
        void apntypeHandler();
        void protocolHandler();
    };

} /* namespace gui */

A module-apps/application-settings-new/widgets/ApnListItem.hpp => module-apps/application-settings-new/widgets/ApnListItem.hpp +18 -0
@@ 0,0 1,18 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-settings-new/data/SettingsItemData.hpp"

namespace gui
{
    class ApnListItem : public ListItem
    {
      public:
        std::function<void(std::shared_ptr<packet_data::APN::Config> apnRecord)> onSaveCallback = nullptr;
        std::function<void(std::shared_ptr<packet_data::APN::Config> apnRecord)> onLoadCallback = nullptr;
        std::function<bool()> onEmptyCallback                                                   = nullptr;
    };

} /* namespace gui */

M module-apps/application-settings-new/widgets/SettingsStyle.hpp => module-apps/application-settings-new/widgets/SettingsStyle.hpp +20 -0
@@ 28,7 28,18 @@ namespace style
                inline constexpr auto before_noon             = "AM";
                inline constexpr auto after_noon              = "PM";
            } // namespace time

            namespace apnInputWidget
            {
                inline constexpr uint32_t w                = style::window::default_body_width;
                inline constexpr uint32_t h                = 63;
                inline constexpr uint32_t title_label_h    = 20;
                inline constexpr uint32_t input_text_h     = 37;
                inline constexpr uint32_t span_size        = 6;
                inline constexpr int32_t underline_padding = 4;
            } // namespace apnInputWidget
        }     // namespace widget

        namespace window
        {
            namespace leftArrowImage


@@ 70,6 81,15 @@ namespace style

                inline constexpr auto separator_h = 55;
            } // namespace nightshift

            namespace newApn
            {
                inline constexpr uint32_t x = style::window::default_left_margin;
                inline constexpr uint32_t y = style::header::height;
                inline constexpr uint32_t w = style::listview::body_width_with_scroll;
                inline constexpr uint32_t h = style::window_height - y - style::footer::height;
            } // namespace newApn

        }     // namespace window
    };        // namespace settings
} // namespace style

M module-apps/application-settings-new/windows/APNSettingsWindow.cpp => module-apps/application-settings-new/windows/APNSettingsWindow.cpp +6 -3
@@ 4,6 4,7 @@
#include "APNSettingsWindow.hpp"
#include "application-settings-new/ApplicationSettings.hpp"
#include "application-settings-new/widgets/SettingsStyle.hpp"
#include "application-settings-new/data/SettingsItemData.hpp"
#include "OptionSetting.hpp"

#include <InputEvent.hpp>


@@ 18,15 19,17 @@ namespace gui

    auto APNSettingsWindow::onInput(const InputEvent &inputEvent) -> bool
    {

        if (inputEvent.isShortPress()) {
            if (inputEvent.is(KeyCode::KEY_LEFT)) {
                // switch to new/edit APN window
                auto apnRecord                        = std::make_shared<packet_data::APN::Config>();
                std::unique_ptr<gui::SwitchData> data = std::make_unique<ApnItemData>(apnRecord);
                application->switchWindow(gui::window::name::new_apn, gui::ShowMode::GUI_SHOW_INIT, std::move(data));
                return true;
            }
        }

        return AppWindow::onInput(inputEvent);
    }

    void APNSettingsWindow::buildInterface()
    {
        setTitle(utils::localize.get("app_settings_network_apn_settings"));

A module-apps/application-settings-new/windows/NewApnWindow.cpp => module-apps/application-settings-new/windows/NewApnWindow.cpp +116 -0
@@ 0,0 1,116 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewApnWindow.hpp"
#include "application-settings-new/ApplicationSettings.hpp"

#include <Dialog.hpp>
#include <messages/DialogMetadataMessage.hpp>

namespace gui
{

    NewApnWindow::NewApnWindow(app::Application *app)
        : AppWindow(app, gui::window::name::new_apn), newApnModel{std::make_shared<NewApnModel>(app)}
    {
        buildInterface();
    }

    void NewApnWindow::rebuild()
    {
        destroyInterface();
        buildInterface();
    }

    void NewApnWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));

        setTitle(utils::localize.get("app_settings_new_edit_apn"));

        list = new gui::ListView(this,
                                 style::settings::window::newApn::x,
                                 style::settings::window::newApn::y,
                                 style::settings::window::newApn::w,
                                 style::settings::window::newApn::h,
                                 newApnModel);
        setFocusItem(list);
    }

    void NewApnWindow::destroyInterface()
    {
        erase();
    }

    void NewApnWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (mode != ShowMode::GUI_SHOW_RETURN) {
            newApnModel->clearData();
        }

        if (mode == ShowMode::GUI_SHOW_INIT) {
            list->rebuildList();
        }

        if (apn != nullptr) {
            newApnModel->loadData(apn);
        }
    }

    auto NewApnWindow::handleSwitchData(SwitchData *data) -> bool
    {
        setSaveButtonVisible(false);

        if (data == nullptr) {
            return false;
        }

        auto *item = dynamic_cast<ApnItemData *>(data);
        if (item == nullptr) {
            return false;
        }

        apn = item->getApn();
        if (apn == nullptr) {
            apn = std::make_shared<packet_data::APN::Config>();
            return true;
        }

        return true;
    }

    void NewApnWindow::setSaveButtonVisible(bool visible)
    {
        bottomBar->setActive(BottomBar::Side::CENTER, visible);
    }

    auto NewApnWindow::onInput(const InputEvent &inputEvent) -> bool
    {
        if (inputEvent.isShortPress()) {
            if (inputEvent.is(KeyCode::KEY_ENTER)) {
                if (apn != nullptr)
                    newApnModel->saveData(apn);
                verifyAndSave();
                return true;
            }
        }

        return AppWindow::onInput(inputEvent);
    }

    auto NewApnWindow::verifyAndSave() -> bool
    {
        if (apn != nullptr) {
            newApnModel->apnSendRecord(*apn);
            LOG_DEBUG("APN record  saved : \"%s\" ", apn->apn.c_str());
        }
        else
            LOG_DEBUG("APN record not found");

        return true;
    }

} // namespace gui

A module-apps/application-settings-new/windows/NewApnWindow.hpp => module-apps/application-settings-new/windows/NewApnWindow.hpp +33 -0
@@ 0,0 1,33 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-settings-new/models/NewApnModel.hpp"

#include <AppWindow.hpp>
#include <ListView.hpp>
#include <Text.hpp>

namespace gui
{
    class NewApnWindow : public AppWindow
    {
      public:
        NewApnWindow(app::Application *app);

      private:
        auto onInput(const InputEvent &inputEvent) -> bool override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto handleSwitchData(SwitchData *data) -> bool override;
        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;
        auto verifyAndSave() -> bool;
        void setSaveButtonVisible(bool visible);
        std::shared_ptr<packet_data::APN::Config> apn;
        std::shared_ptr<NewApnModel> newApnModel;
        gui::ListView *list = nullptr;
    };

} /* namespace gui */

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +5 -5
@@ 297,12 297,12 @@ void ServiceCellular::registerMessageHandlers()
        return handleCellularGetActiveContextsMessage(msg);
    });

    connect(typeid(CellularGetAPNMessage), [&](sys::Message *request) -> sys::MessagePointer {
        connect(typeid(CellularGetCurrentOperatorMessage), [&](sys::Message *request) -> sys::MessagePointer {
            auto msg = static_cast<CellularGetCurrentOperatorMessage *>(request);
            return handleCellularGetCurrentOperator(msg);
        });
    connect(typeid(CellularGetCurrentOperatorMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularGetCurrentOperatorMessage *>(request);
        return handleCellularGetCurrentOperator(msg);
    });

    connect(typeid(CellularGetAPNMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularGetAPNMessage *>(request);
        return handleCellularGetAPNMessage(msg);
    });

M module-services/service-cellular/service-cellular/PacketDataTypes.hpp => module-services/service-cellular/service-cellular/PacketDataTypes.hpp +64 -2
@@ 5,6 5,10 @@

#include <string>
#include <memory>
#include <unordered_map>
#include <map>

#include <Utils.hpp>

namespace packet_data
{


@@ 22,10 26,11 @@ namespace packet_data
         */
        enum class APNType
        {
            Default, ///< for data traffic
            Default, ///< only one APN is set as default
            IMS,     ///< IP Multimedia Subsystem for eg VoLTE
            MMS,     ///< for MMS service
            Fota     ///< for Firmware Update
            Fota,    ///< for Firmware Update
            Internet //< for data traffic
        };

        /**


@@ 81,6 86,63 @@ namespace packet_data
            std::string password;
            std::string ip; /// set after connection

            std::string getAuthMethod()
            {
                return utils::enumToString(authMethod);
            }

            void setAuthMethod(const std::string &str)
            {
                if (str == "NONE")
                    authMethod = AuthMethod::NONE;
                else if (str == "AUTO")
                    authMethod = AuthMethod::AUTO;
                else if (str == "CHAP")
                    authMethod = AuthMethod::CHAP;
                else if (str == "PAP")
                    authMethod = AuthMethod::PAP;
                else
                    authMethod = AuthMethod::NONE;
            }

            std::string getApnType()
            {
                return utils::enumToString(apnType);
            }

            void setApnType(const std::string &str)
            {
                if (str == "Default")
                    apnType = APNType::Default;
                else if (str == "Fota")
                    apnType = APNType::Fota;
                else if (str == "IMS")
                    apnType = APNType::IMS;
                else if (str == "Internet")
                    apnType = APNType::Internet;
                else if (str == "MMS")
                    apnType = APNType::MMS;
                else
                    apnType = APNType::Internet;
            }

            std::string getApnProtocol()
            {
                return utils::enumToString(contextType);
            }

            void setApnProtocol(const std::string &str)
            {
                if (str == "ipv4")
                    contextType = ContextType::ipv4;
                else if (str == "ipv6")
                    contextType = ContextType::ipv6;
                else if (str == "ipv4v6")
                    contextType = ContextType::ipv4v6;
                else
                    contextType = ContextType::ipv4;
            }

            bool isEmpty() const noexcept
            {
                return apn.empty();