Merge branch 'update_touch_driver' of git://github.com/Riksu9000/InfiniTime into Riksu9000-update_touch_driver
# Conflicts: # src/displayapp/Apps.h
This commit is contained in:
		
						commit
						6d0e68d626
					
				
							
								
								
									
										10
									
								
								src/BootErrors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/BootErrors.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace System {
 | 
			
		||||
    enum class BootErrors {
 | 
			
		||||
      None,
 | 
			
		||||
      TouchController,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -421,6 +421,7 @@ list(APPEND SOURCE_FILES
 | 
			
		||||
        displayapp/screens/BatteryInfo.cpp
 | 
			
		||||
        displayapp/screens/Steps.cpp
 | 
			
		||||
        displayapp/screens/Timer.cpp
 | 
			
		||||
        displayapp/screens/Error.cpp
 | 
			
		||||
        displayapp/screens/Alarm.cpp
 | 
			
		||||
        displayapp/Colors.cpp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,8 @@ namespace Pinetime {
 | 
			
		||||
      SettingSteps,
 | 
			
		||||
      SettingPineTimeStyle,
 | 
			
		||||
      SettingSetDate,
 | 
			
		||||
      SettingSetTime
 | 
			
		||||
      SettingSetTime,
 | 
			
		||||
      Error,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@
 | 
			
		||||
#include "displayapp/screens/FlashLight.h"
 | 
			
		||||
#include "displayapp/screens/BatteryInfo.h"
 | 
			
		||||
#include "displayapp/screens/Steps.h"
 | 
			
		||||
#include "displayapp/screens/Error.h"
 | 
			
		||||
 | 
			
		||||
#include "drivers/Cst816s.h"
 | 
			
		||||
#include "drivers/St7789.h"
 | 
			
		||||
@ -112,11 +113,16 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd,
 | 
			
		||||
    touchHandler {touchHandler} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DisplayApp::Start() {
 | 
			
		||||
void DisplayApp::Start(System::BootErrors error) {
 | 
			
		||||
  msgQueue = xQueueCreate(queueSize, itemSize);
 | 
			
		||||
 | 
			
		||||
  // Start clock when smartwatch boots
 | 
			
		||||
  LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
 | 
			
		||||
  bootError = error;
 | 
			
		||||
 | 
			
		||||
  if (error == System::BootErrors::TouchController) {
 | 
			
		||||
    LoadApp(Apps::Error, DisplayApp::FullRefreshDirections::None);
 | 
			
		||||
  } else {
 | 
			
		||||
    LoadApp(Apps::Clock, DisplayApp::FullRefreshDirections::None);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (pdPASS != xTaskCreate(DisplayApp::Process, "displayapp", 800, this, 0, &taskHandle)) {
 | 
			
		||||
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
 | 
			
		||||
@ -311,6 +317,11 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
 | 
			
		||||
                                                       motionController);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case Apps::Error:
 | 
			
		||||
      currentScreen = std::make_unique<Screens::Error>(this, bootError);
 | 
			
		||||
      ReturnApp(Apps::Clock, FullRefreshDirections::Down, TouchEvents::None);
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case Apps::FirmwareValidation:
 | 
			
		||||
      currentScreen = std::make_unique<Screens::FirmwareValidation>(this, validator);
 | 
			
		||||
      ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
 | 
			
		||||
@ -385,7 +396,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
 | 
			
		||||
      break;
 | 
			
		||||
    case Apps::SysInfo:
 | 
			
		||||
      currentScreen = std::make_unique<Screens::SystemInfo>(
 | 
			
		||||
        this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController);
 | 
			
		||||
        this, dateTimeController, batteryController, brightnessController, bleController, watchdog, motionController, touchPanel);
 | 
			
		||||
      ReturnApp(Apps::Settings, FullRefreshDirections::Down, TouchEvents::SwipeDown);
 | 
			
		||||
      break;
 | 
			
		||||
    case Apps::FlashLight:
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@
 | 
			
		||||
#include "touchhandler/TouchHandler.h"
 | 
			
		||||
 | 
			
		||||
#include "Messages.h"
 | 
			
		||||
#include "BootErrors.h"
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
@ -61,7 +62,7 @@ namespace Pinetime {
 | 
			
		||||
                 Pinetime::Controllers::TimerController& timerController,
 | 
			
		||||
                 Pinetime::Controllers::AlarmController& alarmController,
 | 
			
		||||
                 Pinetime::Controllers::TouchHandler& touchHandler);
 | 
			
		||||
      void Start();
 | 
			
		||||
      void Start(System::BootErrors error);
 | 
			
		||||
      void PushMessage(Display::Messages msg);
 | 
			
		||||
 | 
			
		||||
      void StartApp(Apps app, DisplayApp::FullRefreshDirections direction);
 | 
			
		||||
@ -116,6 +117,7 @@ namespace Pinetime {
 | 
			
		||||
 | 
			
		||||
      Apps nextApp = Apps::None;
 | 
			
		||||
      DisplayApp::FullRefreshDirections nextDirection;
 | 
			
		||||
      System::BootErrors bootError;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								src/displayapp/screens/Error.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/displayapp/screens/Error.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
#include "Error.h"
 | 
			
		||||
 | 
			
		||||
using namespace Pinetime::Applications::Screens;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
  void ButtonEventCallback(lv_obj_t* obj, lv_event_t /*event*/) {
 | 
			
		||||
    auto* errorScreen = static_cast<Error*>(obj->user_data);
 | 
			
		||||
    errorScreen->ButtonEventHandler();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error::Error(Pinetime::Applications::DisplayApp* app, System::BootErrors error)
 | 
			
		||||
  : Screen(app) {
 | 
			
		||||
 | 
			
		||||
  lv_obj_t* warningLabel = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_obj_set_style_local_text_color(warningLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
 | 
			
		||||
  lv_label_set_text_static(warningLabel, "Warning");
 | 
			
		||||
  lv_obj_align(warningLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0);
 | 
			
		||||
 | 
			
		||||
  lv_obj_t* causeLabel = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_label_set_long_mode(causeLabel, LV_LABEL_LONG_BREAK);
 | 
			
		||||
  lv_obj_set_width(causeLabel, LV_HOR_RES);
 | 
			
		||||
  lv_obj_align(causeLabel, warningLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
 | 
			
		||||
 | 
			
		||||
  if (error == System::BootErrors::TouchController) {
 | 
			
		||||
    lv_label_set_text_static(causeLabel, "Touch controller error detected.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lv_obj_t* tipLabel = lv_label_create(lv_scr_act(), nullptr);
 | 
			
		||||
  lv_label_set_long_mode(tipLabel, LV_LABEL_LONG_BREAK);
 | 
			
		||||
  lv_obj_set_width(tipLabel, LV_HOR_RES);
 | 
			
		||||
  lv_label_set_text_static(tipLabel, "If you encounter problems and your device is under warranty, contact the devices seller.");
 | 
			
		||||
  lv_obj_align(tipLabel, causeLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
 | 
			
		||||
 | 
			
		||||
  btnOk = lv_btn_create(lv_scr_act(), nullptr);
 | 
			
		||||
  btnOk->user_data = this;
 | 
			
		||||
  lv_obj_set_event_cb(btnOk, ButtonEventCallback);
 | 
			
		||||
  lv_obj_set_size(btnOk, LV_HOR_RES, 50);
 | 
			
		||||
  lv_obj_align(btnOk, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0);
 | 
			
		||||
  lv_obj_set_style_local_value_str(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, "Proceed");
 | 
			
		||||
  lv_obj_set_style_local_bg_color(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Error::ButtonEventHandler() {
 | 
			
		||||
  running = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Error::~Error() {
 | 
			
		||||
  lv_obj_clean(lv_scr_act());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								src/displayapp/screens/Error.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/displayapp/screens/Error.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "Screen.h"
 | 
			
		||||
#include "BootErrors.h"
 | 
			
		||||
#include <lvgl/lvgl.h>
 | 
			
		||||
 | 
			
		||||
namespace Pinetime {
 | 
			
		||||
  namespace Applications {
 | 
			
		||||
    namespace Screens {
 | 
			
		||||
      class Error : public Screen {
 | 
			
		||||
      public:
 | 
			
		||||
        Error(DisplayApp* app, System::BootErrors error);
 | 
			
		||||
        ~Error() override;
 | 
			
		||||
 | 
			
		||||
        void ButtonEventHandler();
 | 
			
		||||
      private:
 | 
			
		||||
        lv_obj_t* btnOk;
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -33,7 +33,8 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
 | 
			
		||||
                       Pinetime::Controllers::BrightnessController& brightnessController,
 | 
			
		||||
                       Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
                       Pinetime::Drivers::WatchdogView& watchdog,
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController)
 | 
			
		||||
                       Pinetime::Controllers::MotionController& motionController,
 | 
			
		||||
                       Pinetime::Drivers::Cst816S& touchPanel)
 | 
			
		||||
  : Screen(app),
 | 
			
		||||
    dateTimeController {dateTimeController},
 | 
			
		||||
    batteryController {batteryController},
 | 
			
		||||
@ -41,6 +42,7 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
 | 
			
		||||
    bleController {bleController},
 | 
			
		||||
    watchdog {watchdog},
 | 
			
		||||
    motionController{motionController},
 | 
			
		||||
    touchPanel{touchPanel},
 | 
			
		||||
    screens {app,
 | 
			
		||||
             0,
 | 
			
		||||
             {[this]() -> std::unique_ptr<Screen> {
 | 
			
		||||
@ -141,7 +143,8 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
 | 
			
		||||
                        "#444444 Battery# %d%%/%03imV\n"
 | 
			
		||||
                        "#444444 Backlight# %s\n"
 | 
			
		||||
                        "#444444 Last reset# %s\n"
 | 
			
		||||
                        "#444444 Accel.# %s\n",
 | 
			
		||||
                        "#444444 Accel.# %s\n"
 | 
			
		||||
                        "#444444 Touch.# %x.%x.%x\n",
 | 
			
		||||
                        dateTimeController.Day(),
 | 
			
		||||
                        static_cast<uint8_t>(dateTimeController.Month()),
 | 
			
		||||
                        dateTimeController.Year(),
 | 
			
		||||
@ -156,7 +159,10 @@ std::unique_ptr<Screen> SystemInfo::CreateScreen2() {
 | 
			
		||||
                        batteryController.Voltage(),
 | 
			
		||||
                        brightnessController.ToString(),
 | 
			
		||||
                        resetReason,
 | 
			
		||||
                        ToString(motionController.DeviceType()));
 | 
			
		||||
                        ToString(motionController.DeviceType()),
 | 
			
		||||
                        touchPanel.GetChipId(),
 | 
			
		||||
                        touchPanel.GetVendorId(),
 | 
			
		||||
                        touchPanel.GetFwVersion());
 | 
			
		||||
  lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, 0, 0);
 | 
			
		||||
  return std::make_unique<Screens::Label>(1, 5, app, label);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,8 @@ namespace Pinetime {
 | 
			
		||||
                            Pinetime::Controllers::BrightnessController& brightnessController,
 | 
			
		||||
                            Pinetime::Controllers::Ble& bleController,
 | 
			
		||||
                            Pinetime::Drivers::WatchdogView& watchdog,
 | 
			
		||||
                            Pinetime::Controllers::MotionController& motionController);
 | 
			
		||||
                            Pinetime::Controllers::MotionController& motionController,
 | 
			
		||||
                            Pinetime::Drivers::Cst816S& touchPanel);
 | 
			
		||||
        ~SystemInfo() override;
 | 
			
		||||
        bool OnTouchEvent(TouchEvents event) override;
 | 
			
		||||
 | 
			
		||||
@ -39,6 +40,7 @@ namespace Pinetime {
 | 
			
		||||
        Pinetime::Controllers::Ble& bleController;
 | 
			
		||||
        Pinetime::Drivers::WatchdogView& watchdog;
 | 
			
		||||
        Pinetime::Controllers::MotionController& motionController;
 | 
			
		||||
        Pinetime::Drivers::Cst816S& touchPanel;
 | 
			
		||||
 | 
			
		||||
        ScreenList<5> screens;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ using namespace Pinetime::Drivers;
 | 
			
		||||
Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Cst816S::Init() {
 | 
			
		||||
bool Cst816S::Init() {
 | 
			
		||||
  nrf_gpio_cfg_output(PinMap::Cst816sReset);
 | 
			
		||||
  nrf_gpio_pin_set(PinMap::Cst816sReset);
 | 
			
		||||
  vTaskDelay(50);
 | 
			
		||||
@ -51,10 +51,27 @@ void Cst816S::Init() {
 | 
			
		||||
  */
 | 
			
		||||
  static constexpr uint8_t irqCtl = 0b01110000;
 | 
			
		||||
  twiMaster.Write(twiAddress, 0xFA, &irqCtl, 1);
 | 
			
		||||
 | 
			
		||||
  // There's mixed information about which register contains which information
 | 
			
		||||
  if (twiMaster.Read(twiAddress, 0xA7, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) {
 | 
			
		||||
    chipId = 0xFF;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (twiMaster.Read(twiAddress, 0xA8, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) {
 | 
			
		||||
    vendorId = 0xFF;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (twiMaster.Read(twiAddress, 0xA9, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) {
 | 
			
		||||
    fwVersion = 0xFF;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return chipId == 0xb4 && vendorId == 0 && fwVersion == 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Cst816S::TouchInfos Cst816S::GetTouchInfo() {
 | 
			
		||||
  Cst816S::TouchInfos info;
 | 
			
		||||
  uint8_t touchData[7];
 | 
			
		||||
 | 
			
		||||
  auto ret = twiMaster.Read(twiAddress, 0, touchData, sizeof(touchData));
 | 
			
		||||
  if (ret != TwiMaster::ErrorCodes::NoError) {
 | 
			
		||||
@ -62,18 +79,17 @@ Cst816S::TouchInfos Cst816S::GetTouchInfo() {
 | 
			
		||||
    return info;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto nbTouchPoints = touchData[2] & 0x0f;
 | 
			
		||||
  // This can only be 0 or 1
 | 
			
		||||
  uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f;
 | 
			
		||||
 | 
			
		||||
  auto xHigh = touchData[touchXHighIndex] & 0x0f;
 | 
			
		||||
  auto xLow = touchData[touchXLowIndex];
 | 
			
		||||
  uint16_t x = (xHigh << 8) | xLow;
 | 
			
		||||
  uint8_t xHigh = touchData[touchXHighIndex] & 0x0f;
 | 
			
		||||
  uint8_t xLow = touchData[touchXLowIndex];
 | 
			
		||||
  info.x = (xHigh << 8) | xLow;
 | 
			
		||||
 | 
			
		||||
  auto yHigh = touchData[touchYHighIndex] & 0x0f;
 | 
			
		||||
  auto yLow = touchData[touchYLowIndex];
 | 
			
		||||
  uint16_t y = (yHigh << 8) | yLow;
 | 
			
		||||
  uint8_t yHigh = touchData[touchYHighIndex] & 0x0f;
 | 
			
		||||
  uint8_t yLow = touchData[touchYLowIndex];
 | 
			
		||||
  info.y = (yHigh << 8) | yLow;
 | 
			
		||||
 | 
			
		||||
  info.x = x;
 | 
			
		||||
  info.y = y;
 | 
			
		||||
  info.touching = (nbTouchPoints > 0);
 | 
			
		||||
  info.gesture = static_cast<Gestures>(touchData[gestureIndex]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,11 +30,20 @@ namespace Pinetime {
 | 
			
		||||
      Cst816S(Cst816S&&) = delete;
 | 
			
		||||
      Cst816S& operator=(Cst816S&&) = delete;
 | 
			
		||||
 | 
			
		||||
      void Init();
 | 
			
		||||
      bool Init();
 | 
			
		||||
      TouchInfos GetTouchInfo();
 | 
			
		||||
      void Sleep();
 | 
			
		||||
      void Wakeup();
 | 
			
		||||
 | 
			
		||||
      uint8_t GetChipId() const {
 | 
			
		||||
        return chipId;
 | 
			
		||||
      }
 | 
			
		||||
      uint8_t GetVendorId() const {
 | 
			
		||||
        return vendorId;
 | 
			
		||||
      }
 | 
			
		||||
      uint8_t GetFwVersion() const {
 | 
			
		||||
        return fwVersion;
 | 
			
		||||
      }
 | 
			
		||||
    private:
 | 
			
		||||
      // Unused/Unavailable commented out
 | 
			
		||||
      static constexpr uint8_t gestureIndex = 1;
 | 
			
		||||
@ -49,9 +58,12 @@ namespace Pinetime {
 | 
			
		||||
      //static constexpr uint8_t touchXYIndex = 7;
 | 
			
		||||
      //static constexpr uint8_t touchMiscIndex = 8;
 | 
			
		||||
 | 
			
		||||
      uint8_t touchData[7];
 | 
			
		||||
      TwiMaster& twiMaster;
 | 
			
		||||
      uint8_t twiAddress;
 | 
			
		||||
 | 
			
		||||
      uint8_t chipId;
 | 
			
		||||
      uint8_t vendorId;
 | 
			
		||||
      uint8_t fwVersion;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@
 | 
			
		||||
#include "drivers/Hrs3300.h"
 | 
			
		||||
#include "drivers/PinMap.h"
 | 
			
		||||
#include "main.h"
 | 
			
		||||
#include "BootErrors.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
@ -116,6 +117,8 @@ void SystemTask::Process(void* instance) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SystemTask::Work() {
 | 
			
		||||
  BootErrors bootError = BootErrors::None;
 | 
			
		||||
 | 
			
		||||
  watchdog.Setup(7);
 | 
			
		||||
  watchdog.Start();
 | 
			
		||||
  NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason()));
 | 
			
		||||
@ -133,7 +136,9 @@ void SystemTask::Work() {
 | 
			
		||||
  lcd.Init();
 | 
			
		||||
 | 
			
		||||
  twiMaster.Init();
 | 
			
		||||
  touchPanel.Init();
 | 
			
		||||
  if (!touchPanel.Init()) {
 | 
			
		||||
    bootError = BootErrors::TouchController;
 | 
			
		||||
  }
 | 
			
		||||
  dateTimeController.Register(this);
 | 
			
		||||
  batteryController.Register(this);
 | 
			
		||||
  motorController.Init();
 | 
			
		||||
@ -151,7 +156,7 @@ void SystemTask::Work() {
 | 
			
		||||
  settingsController.Init();
 | 
			
		||||
 | 
			
		||||
  displayApp.Register(this);
 | 
			
		||||
  displayApp.Start();
 | 
			
		||||
  displayApp.Start(bootError);
 | 
			
		||||
 | 
			
		||||
  heartRateSensor.Init();
 | 
			
		||||
  heartRateSensor.Disable();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user