mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
bin/clang-format-fix
This commit is contained in:
parent
25ef2e4e5d
commit
8e38e1bd49
@ -6,23 +6,22 @@
|
|||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/StringUtils.h"
|
#include "util/StringUtils.h"
|
||||||
|
|
||||||
#define DEVICE_NAME "EPaper"
|
#define DEVICE_NAME "EPaper"
|
||||||
#define SERVICE_UUID "4ae29d01-499a-480a-8c41-a82192105125"
|
#define SERVICE_UUID "4ae29d01-499a-480a-8c41-a82192105125"
|
||||||
#define REQUEST_CHARACTERISTIC_UUID "a00e530d-b48b-48c8-aadb-d062a1b91792"
|
#define REQUEST_CHARACTERISTIC_UUID "a00e530d-b48b-48c8-aadb-d062a1b91792"
|
||||||
#define RESPONSE_CHARACTERISTIC_UUID "0c656023-dee6-47c5-9afb-e601dfbdaa1d"
|
#define RESPONSE_CHARACTERISTIC_UUID "0c656023-dee6-47c5-9afb-e601dfbdaa1d"
|
||||||
|
|
||||||
#define OUTPUT_DIRECTORY "/bt"
|
#define OUTPUT_DIRECTORY "/bt"
|
||||||
#define MAX_FILENAME_LENGTH 200
|
#define MAX_FILENAME_LENGTH 200
|
||||||
|
|
||||||
#define PROTOCOL_ASSERT(cond, fmt, ...) \
|
#define PROTOCOL_ASSERT(cond, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (!(cond)) \
|
if (!(cond)) { \
|
||||||
{ \
|
snprintf(errorMessage, sizeof(errorMessage), fmt, ##__VA_ARGS__); \
|
||||||
snprintf(errorMessage, sizeof(errorMessage), fmt, ##__VA_ARGS__); \
|
intoState(STATE_ERROR); \
|
||||||
intoState(STATE_ERROR); \
|
return; \
|
||||||
return; \
|
} \
|
||||||
} \
|
} while (0)
|
||||||
} while (0)
|
|
||||||
|
|
||||||
void BluetoothActivity::displayTaskTrampoline(void* param) {
|
void BluetoothActivity::displayTaskTrampoline(void* param) {
|
||||||
auto* self = static_cast<BluetoothActivity*>(param);
|
auto* self = static_cast<BluetoothActivity*>(param);
|
||||||
@ -42,33 +41,24 @@ void BluetoothActivity::report() {
|
|||||||
onFileReceived(OUTPUT_DIRECTORY "/" + filename);
|
onFileReceived(OUTPUT_DIRECTORY "/" + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothActivity::startAdvertising() {
|
void BluetoothActivity::startAdvertising() { NimBLEDevice::startAdvertising(); }
|
||||||
NimBLEDevice::startAdvertising();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothActivity::stopAdvertising() {
|
void BluetoothActivity::stopAdvertising() { NimBLEDevice::stopAdvertising(); }
|
||||||
NimBLEDevice::stopAdvertising();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BluetoothActivity::onEnter() {
|
void BluetoothActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
|
|
||||||
NimBLEDevice::init(DEVICE_NAME);
|
NimBLEDevice::init(DEVICE_NAME);
|
||||||
NimBLEServer *pServer = NimBLEDevice::createServer();
|
NimBLEServer* pServer = NimBLEDevice::createServer();
|
||||||
pServer->setCallbacks(&serverCallbacks, false);
|
pServer->setCallbacks(&serverCallbacks, false);
|
||||||
NimBLEService *pService = pServer->createService(SERVICE_UUID);
|
NimBLEService* pService = pServer->createService(SERVICE_UUID);
|
||||||
NimBLECharacteristic *pRequestChar = pService->createCharacteristic(
|
NimBLECharacteristic* pRequestChar =
|
||||||
REQUEST_CHARACTERISTIC_UUID,
|
pService->createCharacteristic(REQUEST_CHARACTERISTIC_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
|
||||||
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR
|
|
||||||
);
|
|
||||||
pRequestChar->setCallbacks(&requestCallbacks);
|
pRequestChar->setCallbacks(&requestCallbacks);
|
||||||
pResponseChar = pService->createCharacteristic(
|
pResponseChar = pService->createCharacteristic(RESPONSE_CHARACTERISTIC_UUID, NIMBLE_PROPERTY::INDICATE);
|
||||||
RESPONSE_CHARACTERISTIC_UUID,
|
|
||||||
NIMBLE_PROPERTY::INDICATE
|
|
||||||
);
|
|
||||||
pService->start();
|
pService->start();
|
||||||
|
|
||||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||||
pAdvertising->setName(DEVICE_NAME);
|
pAdvertising->setName(DEVICE_NAME);
|
||||||
pAdvertising->addServiceUUID(pService->getUUID());
|
pAdvertising->addServiceUUID(pService->getUUID());
|
||||||
pAdvertising->enableScanResponse(true);
|
pAdvertising->enableScanResponse(true);
|
||||||
@ -106,14 +96,12 @@ void BluetoothActivity::intoState(State newState) {
|
|||||||
// we cannot call onFileReceived here directly because it might cause onExit to be called,
|
// we cannot call onFileReceived here directly because it might cause onExit to be called,
|
||||||
// which calls NimBLEDevice::deinit, which cannot be called from inside a NimBLE callback.
|
// which calls NimBLEDevice::deinit, which cannot be called from inside a NimBLE callback.
|
||||||
xTaskCreate(&BluetoothActivity::reportTaskTrampoline, "BluetoothReportTask",
|
xTaskCreate(&BluetoothActivity::reportTaskTrampoline, "BluetoothReportTask",
|
||||||
2048, // Stack size
|
2048, // Stack size
|
||||||
this, // Parameters
|
this, // Parameters
|
||||||
1, // Priority,
|
1, // Priority,
|
||||||
nullptr
|
nullptr);
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case STATE_ERROR:
|
case STATE_ERROR: {
|
||||||
{
|
|
||||||
// caller sets errorMessage
|
// caller sets errorMessage
|
||||||
file.close();
|
file.close();
|
||||||
NimBLEServer* pServer = NimBLEDevice::getServer();
|
NimBLEServer* pServer = NimBLEDevice::getServer();
|
||||||
@ -229,12 +217,8 @@ void BluetoothActivity::render() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw help text at bottom
|
// Draw help text at bottom
|
||||||
const auto labels = mappedInput.mapLabels(
|
const auto labels =
|
||||||
"« Back",
|
mappedInput.mapLabels("« Back", (state == STATE_ERROR || state == STATE_DONE) ? "Restart" : "", "", "");
|
||||||
(state == STATE_ERROR || state == STATE_DONE) ? "Restart" : "",
|
|
||||||
"",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
renderer.drawButtonHints(UI_10_FONT_ID, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
||||||
|
|
||||||
renderer.displayBuffer();
|
renderer.displayBuffer();
|
||||||
@ -266,23 +250,24 @@ void BluetoothActivity::onRequest(const lfbt_message* msg, size_t msg_len) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PROTOCOL_ASSERT((txnId == 0) || (txnId == msg->txnId), "Multiple transfers happening at once (%x != %x)", txnId, msg->txnId);
|
PROTOCOL_ASSERT((txnId == 0) || (txnId == msg->txnId), "Multiple transfers happening at once (%x != %x)", txnId,
|
||||||
|
msg->txnId);
|
||||||
|
|
||||||
switch (msg->type) {
|
switch (msg->type) {
|
||||||
case 0: // client_offer
|
case 0: // client_offer
|
||||||
{
|
{
|
||||||
PROTOCOL_ASSERT(state == STATE_CONNECTED, "Invalid state for client_offer: %d", state);
|
PROTOCOL_ASSERT(state == STATE_CONNECTED, "Invalid state for client_offer: %d", state);
|
||||||
PROTOCOL_ASSERT(msg->body.clientOffer.version == 1, "Unsupported protocol version: %u", msg->body.clientOffer.version);
|
PROTOCOL_ASSERT(msg->body.clientOffer.version == 1, "Unsupported protocol version: %u",
|
||||||
|
msg->body.clientOffer.version);
|
||||||
|
|
||||||
totalBytes = msg->body.clientOffer.bodyLength;
|
totalBytes = msg->body.clientOffer.bodyLength;
|
||||||
|
|
||||||
size_t filenameLength = msg_len - 8 - sizeof(lfbt_msg_client_offer);
|
size_t filenameLength = msg_len - 8 - sizeof(lfbt_msg_client_offer);
|
||||||
std::string originalFilename = StringUtils::sanitizeFilename(
|
std::string originalFilename =
|
||||||
std::string(msg->body.clientOffer.name, filenameLength),
|
StringUtils::sanitizeFilename(std::string(msg->body.clientOffer.name, filenameLength), MAX_FILENAME_LENGTH);
|
||||||
MAX_FILENAME_LENGTH
|
|
||||||
);
|
|
||||||
|
|
||||||
PROTOCOL_ASSERT(SdMan.ensureDirectoryExists(OUTPUT_DIRECTORY), "Couldn't create output directory %s", OUTPUT_DIRECTORY);
|
PROTOCOL_ASSERT(SdMan.ensureDirectoryExists(OUTPUT_DIRECTORY), "Couldn't create output directory %s",
|
||||||
|
OUTPUT_DIRECTORY);
|
||||||
|
|
||||||
// generate unique filepath
|
// generate unique filepath
|
||||||
auto splitName = StringUtils::splitFileName(originalFilename);
|
auto splitName = StringUtils::splitFileName(originalFilename);
|
||||||
@ -300,43 +285,39 @@ void BluetoothActivity::onRequest(const lfbt_message* msg, size_t msg_len) {
|
|||||||
filepath = OUTPUT_DIRECTORY "/" + filename;
|
filepath = OUTPUT_DIRECTORY "/" + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
PROTOCOL_ASSERT(SdMan.openFileForWrite("BT", filepath, file), "Couldn't open file %s for writing", filepath.c_str());
|
PROTOCOL_ASSERT(SdMan.openFileForWrite("BT", filepath, file), "Couldn't open file %s for writing",
|
||||||
// TODO: would be neat to check if we have enough space, but SDCardManager doesn't seem to expose that info currently
|
filepath.c_str());
|
||||||
|
// TODO: would be neat to check if we have enough space, but SDCardManager doesn't seem to expose that info
|
||||||
|
// currently
|
||||||
|
|
||||||
txnId = msg->txnId;
|
txnId = msg->txnId;
|
||||||
|
|
||||||
intoState(STATE_OFFERED);
|
intoState(STATE_OFFERED);
|
||||||
|
|
||||||
lfbt_message response = {
|
lfbt_message response = {.type = 1, // server_response
|
||||||
.type = 1, // server_response
|
.txnId = txnId,
|
||||||
.txnId = txnId,
|
.body = {.serverResponse = {.status = 0}}};
|
||||||
.body = {.serverResponse = {.status = 0}}
|
|
||||||
};
|
|
||||||
pResponseChar->setValue(reinterpret_cast<uint8_t*>(&response), 8 + sizeof(lfbt_msg_server_response));
|
pResponseChar->setValue(reinterpret_cast<uint8_t*>(&response), 8 + sizeof(lfbt_msg_server_response));
|
||||||
pResponseChar->indicate();
|
pResponseChar->indicate();
|
||||||
|
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: // client_chunk
|
case 2: // client_chunk
|
||||||
{
|
{
|
||||||
Serial.printf(
|
Serial.printf("[%lu] [BT] Received client_chunk, offset %u, length %zu\n", millis(), msg->body.clientChunk.offset,
|
||||||
"[%lu] [BT] Received client_chunk, offset %u, length %zu\n",
|
msg_len - 8 - sizeof(lfbt_msg_client_chunk));
|
||||||
millis(),
|
|
||||||
msg->body.clientChunk.offset,
|
|
||||||
msg_len - 8 - sizeof(lfbt_msg_client_chunk)
|
|
||||||
);
|
|
||||||
PROTOCOL_ASSERT(state == STATE_OFFERED || state == STATE_RECEIVING, "Invalid state for client_chunk: %d", state);
|
PROTOCOL_ASSERT(state == STATE_OFFERED || state == STATE_RECEIVING, "Invalid state for client_chunk: %d", state);
|
||||||
PROTOCOL_ASSERT(msg->body.clientChunk.offset == receivedBytes, "Expected chunk %zu, got %u", receivedBytes, msg->body.clientChunk.offset);
|
PROTOCOL_ASSERT(msg->body.clientChunk.offset == receivedBytes, "Expected chunk %zu, got %u", receivedBytes,
|
||||||
|
msg->body.clientChunk.offset);
|
||||||
|
|
||||||
size_t written = file.write(
|
size_t written = file.write(reinterpret_cast<const uint8_t*>(msg->body.clientChunk.body),
|
||||||
reinterpret_cast<const uint8_t*>(msg->body.clientChunk.body),
|
msg_len - 8 - sizeof(lfbt_msg_client_chunk));
|
||||||
msg_len - 8 - sizeof(lfbt_msg_client_chunk)
|
|
||||||
);
|
|
||||||
PROTOCOL_ASSERT(written > 0, "Couldn't write to file");
|
PROTOCOL_ASSERT(written > 0, "Couldn't write to file");
|
||||||
receivedBytes += msg_len - 8 - sizeof(lfbt_msg_client_chunk);
|
receivedBytes += msg_len - 8 - sizeof(lfbt_msg_client_chunk);
|
||||||
if (receivedBytes >= totalBytes) {
|
if (receivedBytes >= totalBytes) {
|
||||||
PROTOCOL_ASSERT(receivedBytes == totalBytes, "Got more bytes than expected: %zu > %zu", receivedBytes, totalBytes);
|
PROTOCOL_ASSERT(receivedBytes == totalBytes, "Got more bytes than expected: %zu > %zu", receivedBytes,
|
||||||
|
totalBytes);
|
||||||
PROTOCOL_ASSERT(file.close(), "Couldn't finalize writing the file");
|
PROTOCOL_ASSERT(file.close(), "Couldn't finalize writing the file");
|
||||||
intoState(STATE_DONE);
|
intoState(STATE_DONE);
|
||||||
} else {
|
} else {
|
||||||
@ -349,12 +330,8 @@ void BluetoothActivity::onRequest(const lfbt_message* msg, size_t msg_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothActivity::RequestCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
void BluetoothActivity::RequestCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) {
|
||||||
const lfbt_message *msg = reinterpret_cast<const lfbt_message*>(pCharacteristic->getValue().data());
|
const lfbt_message* msg = reinterpret_cast<const lfbt_message*>(pCharacteristic->getValue().data());
|
||||||
Serial.printf("[%lu] [BT] Received BLE message of type %u, txnId %x, length %d\n",
|
Serial.printf("[%lu] [BT] Received BLE message of type %u, txnId %x, length %d\n", millis(), msg->type, msg->txnId,
|
||||||
millis(),
|
pCharacteristic->getValue().length());
|
||||||
msg->type,
|
|
||||||
msg->txnId,
|
|
||||||
pCharacteristic->getValue().length()
|
|
||||||
);
|
|
||||||
activity->onRequest(msg, pCharacteristic->getValue().length());
|
activity->onRequest(msg, pCharacteristic->getValue().length());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,31 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <NimBLEDevice.h>
|
||||||
|
#include <NimBLEServer.h>
|
||||||
|
#include <NimBLEUtils.h>
|
||||||
|
#include <SDCardManager.h>
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/semphr.h>
|
#include <freertos/semphr.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
#include <NimBLEDevice.h>
|
|
||||||
#include <NimBLEUtils.h>
|
|
||||||
#include <NimBLEServer.h>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <SDCardManager.h>
|
|
||||||
|
|
||||||
#include "../Activity.h"
|
#include "../Activity.h"
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
uint32_t bodyLength;
|
uint32_t bodyLength;
|
||||||
uint32_t nameLength;
|
uint32_t nameLength;
|
||||||
char name[];
|
char name[];
|
||||||
} lfbt_msg_client_offer; // msg type 0
|
} lfbt_msg_client_offer; // msg type 0
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
} lfbt_msg_server_response; // msg type 1
|
} lfbt_msg_server_response; // msg type 1
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
char body[];
|
char body[];
|
||||||
} lfbt_msg_client_chunk; // msg type 2
|
} lfbt_msg_client_chunk; // msg type 2
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
lfbt_msg_client_offer clientOffer;
|
lfbt_msg_client_offer clientOffer;
|
||||||
@ -62,34 +60,34 @@ class BluetoothActivity final : public Activity {
|
|||||||
void report();
|
void report();
|
||||||
|
|
||||||
void onConnected(bool isConnected);
|
void onConnected(bool isConnected);
|
||||||
void onRequest(const lfbt_message *msg, size_t msg_len);
|
void onRequest(const lfbt_message* msg, size_t msg_len);
|
||||||
|
|
||||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||||
friend class BluetoothActivity;
|
friend class BluetoothActivity;
|
||||||
BluetoothActivity *activity;
|
BluetoothActivity* activity;
|
||||||
|
|
||||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
|
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
|
||||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason);
|
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit ServerCallbacks(BluetoothActivity *activity) : activity(activity) {}
|
explicit ServerCallbacks(BluetoothActivity* activity) : activity(activity) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerCallbacks serverCallbacks;
|
ServerCallbacks serverCallbacks;
|
||||||
|
|
||||||
class RequestCallbacks : public NimBLECharacteristicCallbacks {
|
class RequestCallbacks : public NimBLECharacteristicCallbacks {
|
||||||
friend class BluetoothActivity;
|
friend class BluetoothActivity;
|
||||||
BluetoothActivity *activity;
|
BluetoothActivity* activity;
|
||||||
|
|
||||||
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit RequestCallbacks(BluetoothActivity *activity) : activity(activity) {}
|
explicit RequestCallbacks(BluetoothActivity* activity) : activity(activity) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
RequestCallbacks requestCallbacks;
|
RequestCallbacks requestCallbacks;
|
||||||
|
|
||||||
NimBLECharacteristic *pResponseChar = nullptr;
|
NimBLECharacteristic* pResponseChar = nullptr;
|
||||||
void startAdvertising();
|
void startAdvertising();
|
||||||
void stopAdvertising();
|
void stopAdvertising();
|
||||||
|
|
||||||
@ -115,10 +113,13 @@ class BluetoothActivity final : public Activity {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BluetoothActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
explicit BluetoothActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
||||||
const std::function<void()>& onCancel,
|
const std::function<void()>& onCancel,
|
||||||
const std::function<void(const std::string&)>& onFileReceived)
|
const std::function<void(const std::string&)>& onFileReceived)
|
||||||
: Activity("Bluetooth", renderer, mappedInput), onCancel(onCancel), onFileReceived(onFileReceived),
|
: Activity("Bluetooth", renderer, mappedInput),
|
||||||
serverCallbacks(this), requestCallbacks(this) {}
|
onCancel(onCancel),
|
||||||
|
onFileReceived(onFileReceived),
|
||||||
|
serverCallbacks(this),
|
||||||
|
requestCallbacks(this) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
void onExit() override;
|
void onExit() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|||||||
15
src/main.cpp
15
src/main.cpp
@ -230,17 +230,12 @@ void onGoToFileTransfer() {
|
|||||||
|
|
||||||
void onGoToBluetooth() {
|
void onGoToBluetooth() {
|
||||||
exitActivity();
|
exitActivity();
|
||||||
enterNewActivity(new BluetoothActivity(
|
enterNewActivity(new BluetoothActivity(renderer, mappedInputManager, onGoHome, [](const std::string& filepath) {
|
||||||
renderer,
|
Serial.printf("[%lu] [ ] File received over Bluetooth: %s\n", millis(), filepath.c_str());
|
||||||
mappedInputManager,
|
if (StringUtils::readableFileExtension(filepath)) {
|
||||||
onGoHome,
|
onGoToReader(filepath, MyLibraryActivity::Tab::Recent);
|
||||||
[](const std::string& filepath) {
|
|
||||||
Serial.printf("[%lu] [ ] File received over Bluetooth: %s\n", millis(), filepath.c_str());
|
|
||||||
if (StringUtils::readableFileExtension(filepath)) {
|
|
||||||
onGoToReader(filepath, MyLibraryActivity::Tab::Recent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onGoToSettings() {
|
void onGoToSettings() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user