2022-06-17 20:59:21 -07:00
# include "userapps.h"
# include "ui_userapps.h"
# include "mainwindow.h"
# include "generaldialog.h"
userapps : : userapps ( QWidget * parent ) :
QWidget ( parent ) ,
ui ( new Ui : : userapps )
{
ui - > setupUi ( this ) ;
ui - > launchBtn - > setProperty ( " type " , " borderless " ) ;
ui - > launchBtn - > setStyleSheet ( " background: lightGrey; font-size: 9pt; padding: 8px " ) ;
ui - > launchBtn - > setProperty ( " type " , " borderless " ) ;
ui - > iconBtn - > setProperty ( " type " , " borderless " ) ;
2022-06-18 07:05:03 -07:00
ui - > iconBtn - > setStyleSheet ( " QPushButton[type='borderless']:pressed { background: white; color: white; border: none; } " ) ;
2022-06-17 20:59:21 -07:00
ui - > infoBtn - > setProperty ( " type " , " borderless " ) ;
ui - > statusBtn - > setProperty ( " type " , " borderless " ) ;
ui - > statusBtn - > setStyleSheet ( " background: lightGrey; font-size: 9pt; padding: 8px " ) ;
}
userapps : : ~ userapps ( )
{
delete ui ;
}
void userapps : : provideInfo ( QJsonObject jsonInfo )
{
QString name = jsonInfo [ " Name " ] . toString ( ) ;
appName = name ; // It is for searching for json entry while disabling / enabling
jsonObject = jsonInfo ;
// Limit name size to avoid breaking the GUI
2022-06-18 06:26:52 -07:00
if ( name . size ( ) > 20 ) {
2022-06-17 20:59:21 -07:00
// If someone wants to break the GUI, they will do it ¯\^-^/¯
name . remove ( 16 , 100 ) ;
}
ui - > appNameLabel - > setText ( name ) ;
appDir . setPath ( " /mnt/onboard/onboard/.apps/ " + name + " / " + name ) ;
QFile iconPath = QFile { appDir . path ( ) + " / " + jsonInfo [ " IconPath " ] . toString ( ) } ;
if ( iconPath . exists ( ) = = true ) {
QIcon appIcon = QIcon ( iconPath . fileName ( ) ) ;
ui - > iconBtn - > setIconSize ( QSize ( 40 , 40 ) ) ;
ui - > iconBtn - > setIcon ( appIcon ) ;
}
else {
QString function = __func__ ;
QString message = " : Warning: Icon not found: " ;
message . append ( iconPath . fileName ( ) ) ;
log ( function + message , className ) ;
2022-06-18 21:14:42 -07:00
ui - > iconBtn - > deleteLater ( ) ;
ui - > gridLayout - > deleteLater ( ) ;
2022-06-17 20:59:21 -07:00
}
2022-06-22 21:26:49 -07:00
execPath . setFileName ( jsonInfo [ " ExecPath " ] . toString ( ) ) ;
2022-06-17 20:59:21 -07:00
userAppEnabled = jsonInfo [ " Enabled " ] . toBool ( ) ;
if ( userAppEnabled = = true ) {
ui - > statusBtn - > setText ( " Disable " ) ;
}
else {
ui - > statusBtn - > setText ( " Enable " ) ;
}
}
// This function is needed when we dont want to repaint all widgets, but only change the the page (when no changes to the main JSON file were applied)
void userapps : : changePageEnabling ( bool SecondPage )
{
if ( SecondPage = = true ) {
ui - > stackedWidget - > setCurrentIndex ( 1 ) ;
}
else {
ui - > stackedWidget - > setCurrentIndex ( 0 ) ;
}
}
void userapps : : on_infoBtn_clicked ( )
{
log ( " Launching user application information dialog " , className ) ;
// https://stackoverflow.com/questions/28181627/how-to-convert-a-qjsonobject-to-qstring
QString jsonStringParsed = parseJsonShow ( jsonObject ) ;
global : : text : : textBrowserContents = jsonStringParsed ;
global : : userApps : : appInfoDialog = true ;
generalDialogWindow = new generalDialog ( ) ;
generalDialogWindow - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
generalDialogWindow - > show ( ) ;
}
void userapps : : on_statusBtn_clicked ( )
{
ui - > statusBtn - > setEnabled ( false ) ;
2022-06-18 06:26:52 -07:00
// Here the text on this button is used as a boolean; no need to create a new one
2022-06-17 20:59:21 -07:00
// Disable and Enable
if ( userAppEnabled = = false ) {
userAppEnabled = true ;
ui - > statusBtn - > setText ( " Disable " ) ;
}
else {
userAppEnabled = false ;
ui - > statusBtn - > setText ( " Enable " ) ;
}
QJsonObject jsonRootObject = jsonDocument . object ( ) ;
QJsonArray jsonArrayList = jsonRootObject [ " list " ] . toArray ( ) ;
int arraySize = jsonArrayList . size ( ) ;
2022-06-18 06:26:52 -07:00
for ( int i = 0 ; i < arraySize ; i + + ) {
2022-06-17 20:59:21 -07:00
QJsonObject jsonObject = jsonArrayList . at ( i ) . toObject ( ) ;
QString entryName = jsonObject [ " Name " ] . toString ( ) ;
2022-06-18 06:26:52 -07:00
if ( entryName = = appName ) {
2022-06-17 20:59:21 -07:00
jsonObject . insert ( " Enabled " , QJsonValue ( userAppEnabled ) ) ;
QJsonArray sonArrayListNew = jsonDocument . object ( ) [ " list " ] . toArray ( ) ;
sonArrayListNew . replace ( i , jsonObject ) ;
jsonRootObject [ " list " ] = sonArrayListNew ;
jsonDocument . setObject ( jsonRootObject ) ;
emit updateJsonFileSignalUA ( jsonDocument ) ;
}
}
ui - > statusBtn - > setEnabled ( true ) ;
}
void userapps : : updateJsonFileSlotUA ( QJsonDocument jsonDocumentProvided )
{
jsonDocument = jsonDocumentProvided ;
}
void userapps : : on_launchBtn_clicked ( )
{
// Some command to execute script or binary at "ExecPath"
2022-06-20 15:12:20 -07:00
QJsonArray supportedDevices = jsonObject [ " SupportedDevices " ] . toArray ( ) ;
2022-06-17 20:59:21 -07:00
2022-06-20 15:12:20 -07:00
// This will work even if we are looking for 'n306' and there is a device named 'n306b' because QJsonArray::contains() works that way
2022-06-17 20:59:21 -07:00
if ( supportedDevices . contains ( " all " ) = = false and supportedDevices . contains ( global : : deviceID . trimmed ( ) ) = = false ) {
2022-06-22 21:26:49 -07:00
log ( " Warning: User application ' " + appName + " ' does not support this device " , className ) ;
2022-06-17 20:59:21 -07:00
global : : userApps : : appCompatibilityDialog = true ;
2022-06-20 15:12:20 -07:00
global : : userApps : : appCompatibilityText = " <font face='u001'>Your device is not compatible with this app.<br>Continue anyway</font><font face='Inter'>?</font> " ;
2022-06-17 20:59:21 -07:00
generalDialogWindow = new generalDialog ( ) ;
generalDialogWindow - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
generalDialogWindow - > exec ( ) ;
}
else {
global : : userApps : : launchApp = true ;
}
if ( manageRequiredFeatures ( ) = = true ) {
if ( global : : userApps : : launchApp = = true ) {
global : : userApps : : launchApp = false ;
2022-07-06 11:24:23 -07:00
QString message = " Launching user application at: ' " ;
2022-06-22 21:26:49 -07:00
message . append ( " [ " + appDir . path ( ) + " ] " + execPath . fileName ( ) ) ;
2022-07-06 11:24:23 -07:00
message . append ( " ' " ) ;
2022-06-17 20:59:21 -07:00
log ( message , className ) ;
2022-06-20 09:57:55 -07:00
// Tell the OS that we're not running anymore
string_writeconfig ( " /tmp/inkbox_running " , " false " ) ;
2022-06-17 20:59:21 -07:00
QProcess process ;
QStringList args ;
args < < appDir . path ( ) < < execPath . fileName ( ) ;
process . startDetached ( " launch_user_application.sh " , args ) ;
qApp - > quit ( ) ;
}
}
}
bool userapps : : manageRequiredFeatures ( )
{
2022-06-20 15:12:20 -07:00
// This should be already set to 'true', but just in case
global : : userApps : : appCompatibilityLastContinueStatus = true ;
2022-06-17 20:59:21 -07:00
QJsonArray jsonArray = jsonObject [ " RequiredFeatures " ] . toArray ( ) ;
2022-06-18 06:26:52 -07:00
for ( QJsonValueRef refJsonObject : jsonArray ) {
2022-06-17 20:59:21 -07:00
bool launchDialog = false ;
int featureId = refJsonObject . toInt ( ) ;
// Wi-Fi connection required
if ( featureId = = 0 ) {
2022-06-20 15:12:20 -07:00
// Double 'if' conditions to avoid launching unnecesery testPing() in emu
if ( global : : deviceID ! = " emu \n " ) {
if ( testPing ( true ) ! = 0 ) {
global : : userApps : : appCompatibilityText = " <font face='u001'>This app needs a Wi-Fi connection, continue anyway</font><font face='Inter'>?</font> " ;
launchDialog = true ;
}
}
2022-06-17 20:59:21 -07:00
}
// Rooted kernel required
if ( featureId = = 1 ) {
2022-06-20 15:12:20 -07:00
if ( checkconfig ( " /external_root/opt/root/rooted " ) = = true ) {
global : : userApps : : appCompatibilityText = " <font face='u001'>This app needs a rooted kernel, continue anyway</font><font face='Inter'>?</font> " ;
launchDialog = true ;
}
2022-06-17 20:59:21 -07:00
}
2022-06-20 15:12:20 -07:00
// Pseudoterminal support (ID: 2) is managed by the 'gui_apps' service (https://github.com/Kobo-InkBox/rootfs/blob/master/etc/init.d/gui_apps)
2022-06-17 20:59:21 -07:00
2022-06-18 06:26:52 -07:00
if ( launchDialog = = true ) {
2022-06-17 20:59:21 -07:00
global : : userApps : : appCompatibilityDialog = true ;
generalDialogWindow = new generalDialog ( ) ;
generalDialogWindow - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
generalDialogWindow - > exec ( ) ;
}
2022-06-18 06:26:52 -07:00
if ( global : : userApps : : appCompatibilityLastContinueStatus = = false ) {
2022-06-17 20:59:21 -07:00
return false ;
}
}
return true ;
}
QString userapps : : parseJsonShow ( QJsonObject json )
{
QString mainString ;
foreach ( const QString & key , json . keys ( ) ) {
QJsonValue value = json . value ( key ) ;
QString appendString ;
appendString . append ( " <b> " ) ;
appendString . append ( key ) ;
appendString . append ( " </b>: " ) ;
if ( value . isString ( ) ) {
appendString . append ( value . toString ( ) ) ;
}
else if ( value . isBool ( ) ) {
appendString . append ( QVariant ( value . toBool ( ) ) . toString ( ) ) ;
}
else if ( value . isArray ( ) ) {
QJsonArray array = value . toArray ( ) ;
2022-06-20 15:12:20 -07:00
if ( key = = " RequiredFeatures " ) {
2022-06-22 21:56:07 -07:00
bool foundRequiredFeature = false ;
appendString . append ( " None " ) ;
2022-06-20 15:12:20 -07:00
for ( QJsonValueRef ref : array ) {
2022-06-22 21:56:07 -07:00
foundRequiredFeature = true ;
appendString . remove ( appendString . size ( ) - 4 , 4 ) ;
2022-06-20 15:12:20 -07:00
int id = ref . toInt ( ) ;
if ( id = = 0 ) {
appendString . append ( " Wi-Fi connection " ) ;
}
else if ( id = = 1 ) {
appendString . append ( " Rooted kernel " ) ;
}
else if ( id = = 2 ) {
appendString . append ( " Pseudoterminal support " ) ;
}
appendString . append ( " , " ) ;
2022-06-18 06:26:52 -07:00
}
2022-06-22 21:56:07 -07:00
if ( foundRequiredFeature = = true ) {
appendString . remove ( appendString . size ( ) - 2 , 2 ) ;
}
2022-06-20 15:12:20 -07:00
}
else if ( key = = " SupportedDevices " ) {
for ( QJsonValueRef ref : array ) {
QString name = ref . toString ( ) ;
appendString . append ( name ) ;
appendString . append ( " , " ) ;
2022-06-17 20:59:21 -07:00
}
2022-06-20 15:12:20 -07:00
appendString . remove ( appendString . size ( ) - 2 , 2 ) ;
2022-06-17 20:59:21 -07:00
}
2022-06-20 15:12:20 -07:00
}
2022-06-17 20:59:21 -07:00
appendString . append ( " <br> " ) ;
mainString . append ( appendString ) ;
}
return mainString ;
}