Merge branch 'qml_app_avatar_picker' into qml_app_ui_aesthetic

This commit is contained in:
Angela Mazzurco 2017-07-19 14:45:19 +02:00
commit 6f81662428
20 changed files with 463 additions and 101 deletions

View File

@ -161,6 +161,7 @@ ChatHandler::ChatHandler(StateTokenServer *sts, RsNotify *notify, RsMsgs *msgs,
mMsgStateToken = mStateTokenServer->getNewToken(); mMsgStateToken = mStateTokenServer->getNewToken();
mLobbiesStateToken = mStateTokenServer->getNewToken(); mLobbiesStateToken = mStateTokenServer->getNewToken();
mUnreadMsgsStateToken = mStateTokenServer->getNewToken(); mUnreadMsgsStateToken = mStateTokenServer->getNewToken();
mInvitationsStateToken = mStateTokenServer->getNewToken();
addResourceHandler("*", this, &ChatHandler::handleWildcard); addResourceHandler("*", this, &ChatHandler::handleWildcard);
addResourceHandler("lobbies", this, &ChatHandler::handleLobbies); addResourceHandler("lobbies", this, &ChatHandler::handleLobbies);
@ -169,6 +170,9 @@ ChatHandler::ChatHandler(StateTokenServer *sts, RsNotify *notify, RsMsgs *msgs,
addResourceHandler("unsubscribe_lobby", this, &ChatHandler::handleUnsubscribeLobby); addResourceHandler("unsubscribe_lobby", this, &ChatHandler::handleUnsubscribeLobby);
addResourceHandler("autosubscribe_lobby", this, &ChatHandler::handleAutoSubsribeLobby); addResourceHandler("autosubscribe_lobby", this, &ChatHandler::handleAutoSubsribeLobby);
addResourceHandler("clear_lobby", this, &ChatHandler::handleClearLobby); addResourceHandler("clear_lobby", this, &ChatHandler::handleClearLobby);
addResourceHandler("invite_to_lobby", this, &ChatHandler::handleInviteToLobby);
addResourceHandler("get_invitations_to_lobby", this, &ChatHandler::handleGetInvitationsToLobby);
addResourceHandler("answer_to_invitation", this, &ChatHandler::handleAnswerToInvitation);
addResourceHandler("lobby_participants", this, &ChatHandler::handleLobbyParticipants); addResourceHandler("lobby_participants", this, &ChatHandler::handleLobbyParticipants);
addResourceHandler("messages", this, &ChatHandler::handleMessages); addResourceHandler("messages", this, &ChatHandler::handleMessages);
addResourceHandler("send_message", this, &ChatHandler::handleSendMessage); addResourceHandler("send_message", this, &ChatHandler::handleSendMessage);
@ -229,6 +233,15 @@ void ChatHandler::notifyChatLobbyEvent(uint64_t lobby_id, uint32_t event_type,
} }
} }
void ChatHandler::notifyListChange(int list, int type)
{
if(list == NOTIFY_LIST_CHAT_LOBBY_INVITATION)
{
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
mStateTokenServer->replaceToken(mInvitationsStateToken);
}
}
void ChatHandler::tick() void ChatHandler::tick()
{ {
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/ RS_STACK_MUTEX(mMtx); /********** LOCKED **********/
@ -936,6 +949,70 @@ void ChatHandler::handleClearLobby(Request &req, Response &resp)
resp.setOk(); resp.setOk();
} }
void ChatHandler::handleInviteToLobby(Request& req, Response& resp)
{
std::string chat_id;
std::string pgp_id;
req.mStream << makeKeyValueReference("chat_id", chat_id);
req.mStream << makeKeyValueReference("pgp_id", pgp_id);
ChatId chatId(chat_id);
RsPgpId pgpId(pgp_id);
std::list<RsPeerId> peerIds;
mRsPeers->getAssociatedSSLIds(pgpId, peerIds);
for(std::list<RsPeerId>::iterator it = peerIds.begin(); it != peerIds.end(); it++)
mRsMsgs->invitePeerToLobby(chatId.toLobbyId(), (*it));
resp.setOk();
}
void ChatHandler::handleGetInvitationsToLobby(Request& req, Response& resp)
{
std::list<ChatLobbyInvite> invites;
mRsMsgs->getPendingChatLobbyInvites(invites);
resp.mDataStream.getStreamToMember();
for(std::list<ChatLobbyInvite>::const_iterator it = invites.begin(); it != invites.end(); ++it)
{
resp.mDataStream.getStreamToMember()
<< makeKeyValue("peer_id", (*it).peer_id.toStdString())
<< makeKeyValue("lobby_id", (*it).lobby_id)
<< makeKeyValue("lobby_name", (*it).lobby_name)
<< makeKeyValue("lobby_topic", (*it).lobby_topic);
}
resp.mStateToken = mInvitationsStateToken;
resp.setOk();
}
void ChatHandler::handleAnswerToInvitation(Request& req, Response& resp)
{
ChatLobbyId lobbyId = 0;
req.mStream << makeKeyValueReference("lobby_id", lobbyId);
bool join;
req.mStream << makeKeyValueReference("join", join);
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
RsGxsId gxsId(gxs_id);
if(join)
{
if(rsMsgs->acceptLobbyInvite(lobbyId, gxsId))
resp.setOk();
else
resp.setFail();
}
else
{
rsMsgs->denyLobbyInvite(lobbyId);
resp.setOk();
}
}
ResponseTask* ChatHandler::handleLobbyParticipants(Request &req, Response &resp) ResponseTask* ChatHandler::handleLobbyParticipants(Request &req, Response &resp)
{ {
RS_STACK_MUTEX(mMtx); /********** LOCKED **********/ RS_STACK_MUTEX(mMtx); /********** LOCKED **********/

View File

@ -35,6 +35,8 @@ public:
virtual void notifyChatLobbyEvent (uint64_t /* lobby id */, uint32_t /* event type */ , virtual void notifyChatLobbyEvent (uint64_t /* lobby id */, uint32_t /* event type */ ,
const RsGxsId& /* nickname */,const std::string& /* any string */); const RsGxsId& /* nickname */,const std::string& /* any string */);
virtual void notifyListChange(int list, int type);
// from tickable // from tickable
virtual void tick(); virtual void tick();
@ -122,6 +124,9 @@ private:
void handleSubscribeLobby(Request& req, Response& resp); void handleSubscribeLobby(Request& req, Response& resp);
void handleUnsubscribeLobby(Request& req, Response& resp); void handleUnsubscribeLobby(Request& req, Response& resp);
void handleAutoSubsribeLobby(Request& req, Response& resp); void handleAutoSubsribeLobby(Request& req, Response& resp);
void handleInviteToLobby(Request& req, Response& resp);
void handleGetInvitationsToLobby(Request& req, Response& resp);
void handleAnswerToInvitation(Request& req, Response& resp);
void handleClearLobby(Request& req, Response& resp); void handleClearLobby(Request& req, Response& resp);
ResponseTask* handleLobbyParticipants(Request& req, Response& resp); ResponseTask* handleLobbyParticipants(Request& req, Response& resp);
void handleMessages(Request& req, Response& resp); void handleMessages(Request& req, Response& resp);
@ -162,6 +167,7 @@ private:
std::map<ChatLobbyId, LobbyParticipantsInfo> mLobbyParticipantsInfos; std::map<ChatLobbyId, LobbyParticipantsInfo> mLobbyParticipantsInfos;
StateToken mUnreadMsgsStateToken; StateToken mUnreadMsgsStateToken;
StateToken mInvitationsStateToken;
}; };
} // namespace resource_api } // namespace resource_api

View File

@ -601,19 +601,30 @@ void PeersHandler::handleWildcard(Request &req, Response &resp)
} }
RsPeerId peer_id; RsPeerId peer_id;
RsPgpId pgp_id; RsPgpId pgp_id;
std::string cleanCert;
int error_code;
std::string error_string; std::string error_string;
if(mRsPeers->loadCertificateFromString(cert_string, peer_id, pgp_id, error_string)
&& mRsPeers->addFriend(peer_id, pgp_id, flags)) if (mRsPeers->cleanCertificate(cert_string, cleanCert, error_code))
{ {
ok = true; if(mRsPeers->loadCertificateFromString(cert_string, peer_id, pgp_id, error_string)
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id); && mRsPeers->addFriend(peer_id, pgp_id, flags))
resp.mDataStream << makeKeyValueReference("peer_id", peer_id); {
} ok = true;
else resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id);
{ resp.mDataStream << makeKeyValueReference("peer_id", peer_id);
resp.mDebug << "Error: failed to add peer" << std::endl; }
resp.mDebug << error_string << std::endl; else
} {
resp.mDebug << "Error: failed to add peer" << std::endl;
resp.mDebug << error_string << std::endl;
}
}
else
{
resp.mDebug << "Error: failed to add peer" << std::endl;
resp.mDebug << error_code << std::endl;
}
} }
} }
if(ok) if(ok)

View File

@ -868,7 +868,7 @@ bool ftController::alreadyHaveFile(const RsFileHash& hash, FileInfo &info)
return true ; return true ;
// check for file lists // check for file lists
if (mSearch) return false; if (!mSearch) return false;
if (mSearch->search(hash, RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_SPEC_ONLY, info)) if (mSearch->search(hash, RS_FILE_HINTS_LOCAL | RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_SPEC_ONLY, info))
return true ; return true ;

View File

@ -31,7 +31,7 @@
bool GxsTokenQueue::queueRequest(uint32_t token, uint32_t req_type) bool GxsTokenQueue::queueRequest(uint32_t token, uint32_t req_type)
{ {
RsStackMutex stack(mQueueMtx); /********** STACK LOCKED MTX ******/ RS_STACK_MUTEX(mQueueMtx);
mQueue.push_back(GxsTokenQueueItem(token, req_type)); mQueue.push_back(GxsTokenQueueItem(token, req_type));
return true; return true;
} }

View File

@ -1188,26 +1188,30 @@ bool RsGenExchange::getGroupMeta(const uint32_t &token, std::list<RsGroupMetaDat
std::list<RsGxsGrpMetaData*> metaL; std::list<RsGxsGrpMetaData*> metaL;
bool ok = mDataAccess->getGroupSummary(token, metaL); bool ok = mDataAccess->getGroupSummary(token, metaL);
std::list<RsGxsGrpMetaData*>::iterator lit = metaL.begin();
RsGroupMetaData m; RsGroupMetaData m;
for(; lit != metaL.end(); ++lit)
for( std::list<RsGxsGrpMetaData*>::iterator lit = metaL.begin(); lit != metaL.end(); ++lit)
{ {
RsGxsGrpMetaData& gMeta = *(*lit); RsGxsGrpMetaData& gMeta = *(*lit);
m = gMeta; m = gMeta;
RsGroupNetworkStats sts ; RsGroupNetworkStats sts ;
if(mNetService != NULL && mNetService->getGroupNetworkStats((*lit)->mGroupId,sts)) if(mNetService != NULL && mNetService->getGroupNetworkStats(gMeta.mGroupId,sts))
{ {
m.mPop = sts.mSuppliers ; m.mPop = sts.mSuppliers ;
m.mVisibleMsgCount = sts.mMaxVisibleCount ; m.mVisibleMsgCount = sts.mMaxVisibleCount ;
}
else
{
m.mPop= 0 ;
m.mVisibleMsgCount = 0 ;
}
groupInfo.push_back(m); if((!(IS_GROUP_SUBSCRIBED(gMeta.mSubscribeFlags))) || gMeta.mLastPost == 0)
m.mLastPost = sts.mLastGroupModificationTS ;
}
else
{
m.mPop= 0 ;
m.mVisibleMsgCount = 0 ;
}
groupInfo.push_back(m);
delete (*lit); delete (*lit);
} }
@ -1369,6 +1373,14 @@ bool RsGenExchange::getGroupData(const uint32_t &token, std::vector<RsGxsGrpItem
{ {
gItem->meta.mPop = sts.mSuppliers; gItem->meta.mPop = sts.mSuppliers;
gItem->meta.mVisibleMsgCount = sts.mMaxVisibleCount; gItem->meta.mVisibleMsgCount = sts.mMaxVisibleCount;
// When the group is not subscribed, the last post value is not updated, because there's no message stored. As a consequence,
// we rely on network statistics to give this value, but it is not as accurate as if it was locally computed, because of blocked
// posts, friends not available, sync delays, etc. Similarly if the group has just been subscribed, the last post info is probably
// uninitialised, so we will it too.
if((!(IS_GROUP_SUBSCRIBED(gItem->meta.mSubscribeFlags))) || gItem->meta.mLastPost == 0)
gItem->meta.mLastPost = sts.mLastGroupModificationTS ;
} }
else else
{ {
@ -1376,12 +1388,6 @@ bool RsGenExchange::getGroupData(const uint32_t &token, std::vector<RsGxsGrpItem
gItem->meta.mVisibleMsgCount = 0; gItem->meta.mVisibleMsgCount = 0;
} }
// When the group is not subscribed, the last post value is not updated, because there's no message stored. As a consequence,
// we rely on network statistics to give this value, but it is not as accurate as if it was locally computed, because of blocked
// posts, friends not available, sync delays, etc.
if(!(IS_GROUP_SUBSCRIBED(gItem->meta.mSubscribeFlags)))
gItem->meta.mLastPost = sts.mLastGroupModificationTS ;
// Also check the group privacy flags. A while ago, it as possible to publish a group without privacy flags. Now it is not possible anymore. // Also check the group privacy flags. A while ago, it as possible to publish a group without privacy flags. Now it is not possible anymore.
// As a consequence, it's important to supply a correct value in this flag before the data can be edited/updated. // As a consequence, it's important to supply a correct value in this flag before the data can be edited/updated.

View File

@ -4397,32 +4397,31 @@ void p3IdService::handleResponse(uint32_t token, uint32_t req_type)
// stuff. // stuff.
switch(req_type) switch(req_type)
{ {
case GXSIDREQ_CACHEOWNIDS: case GXSIDREQ_CACHEOWNIDS:
cache_load_ownids(token); cache_load_ownids(token);
break; break;
case GXSIDREQ_CACHELOAD: case GXSIDREQ_CACHELOAD:
cache_load_for_token(token); cache_load_for_token(token);
break; break;
case GXSIDREQ_PGPHASH: case GXSIDREQ_PGPHASH:
pgphash_handlerequest(token); pgphash_handlerequest(token);
break; break;
case GXSIDREQ_RECOGN: case GXSIDREQ_RECOGN:
recogn_handlerequest(token); recogn_handlerequest(token);
break; break;
case GXSIDREQ_CACHETEST: case GXSIDREQ_CACHETEST:
cachetest_handlerequest(token); cachetest_handlerequest(token);
break; break;
case GXSIDREQ_OPINION: case GXSIDREQ_OPINION:
opinion_handlerequest(token); opinion_handlerequest(token);
break; break;
case GXSIDREQ_SERIALIZE_TO_MEMORY: case GXSIDREQ_SERIALIZE_TO_MEMORY:
handle_get_serialized_grp(token) ; handle_get_serialized_grp(token);
break;
default: default:
/* error */ std::cerr << "p3IdService::handleResponse() Unknown Request Type: "
std::cerr << "p3IdService::handleResponse() Unknown Request Type: " << req_type; << req_type << std::endl;
std::cerr << std::endl; break;
break;
} }
} }

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1048</width> <width>1573</width>
<height>779</height> <height>1177</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -283,8 +283,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>717</width> <width>1372</width>
<height>692</height> <height>1000</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="scrollAreaWidgetContentsVLayout"> <layout class="QVBoxLayout" name="scrollAreaWidgetContentsVLayout">
@ -569,8 +569,9 @@ border-image: url(:/images/closepressed.png)
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the average of your friend's opinions. If not, your own opinion gives the score.&lt;/p&gt; &lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the difference between friend's positive and negative opinions. If not, your own opinion gives the score.&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -0.6, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a higher reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 30 days). &lt;/p&gt; &lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -1, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a non negative reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 5 days).&lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;You can change the thresholds and the time of inactivity to delete identities in preferences -&amp;gt; people. &lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>

View File

@ -1275,8 +1275,10 @@ static void processList(const QStringList &list, const QString &textSingular, co
/* make path for downloaded file */ /* make path for downloaded file */
std::string path; std::string path;
path = fi.path;//Shared files has path with filename included path = fi.path;//Shared files has path with filename included
if (fi.downloadStatus == FT_STATE_COMPLETE)
path = fi.path + "/" + fi.fname; //Seems that all FileInfo get .path==filepath+filename
//if (fi.downloadStatus == FT_STATE_COMPLETE)
// path = fi.path + "/" + fi.fname;
QFileInfo qinfo; QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(path.c_str())); qinfo.setFile(QString::fromUtf8(path.c_str()));

View File

@ -22,6 +22,7 @@
#include "ChannelPage.h" #include "ChannelPage.h"
#include "rsharesettings.h" #include "rsharesettings.h"
#include "util/misc.h" #include "util/misc.h"
#include "gui/notifyqt.h"
ChannelPage::ChannelPage(QWidget * parent, Qt::WindowFlags flags) ChannelPage::ChannelPage(QWidget * parent, Qt::WindowFlags flags)
: ConfigPage(parent, flags) : ConfigPage(parent, flags)
@ -33,10 +34,14 @@ ChannelPage::ChannelPage(QWidget * parent, Qt::WindowFlags flags)
ui.groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each channel in a new tab")); ui.groupFrameSettingsWidget->setOpenAllInNewTabText(tr("Open each channel in a new tab"));
ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Channel) ; ui.groupFrameSettingsWidget->setType(GroupFrameSettings::Channel) ;
connect(ui.loadThreadCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateLoadThread)) ; connect(ui.loadThreadCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateLoadThread())) ;
} }
void ChannelPage::updateLoadThread() { Settings->setChannelLoadThread(ui.loadThreadCheckBox->isChecked()); } void ChannelPage::updateLoadThread()
{
Settings->setChannelLoadThread(ui.loadThreadCheckBox->isChecked());
NotifyQt::getInstance()->notifySettingsChanged();
}
ChannelPage::~ChannelPage() ChannelPage::~ChannelPage()
{ {

View File

@ -1066,7 +1066,7 @@ void RshareSettings::setForumLoadEmoticons(bool value)
/* Channel */ /* Channel */
bool RshareSettings::getChannelLoadThread() bool RshareSettings::getChannelLoadThread()
{ {
return valueFromGroup("Channel", "LoadThread", true).toBool(); return valueFromGroup("Channel", "LoadThread", false).toBool();
} }
void RshareSettings::setChannelLoadThread(bool value) void RshareSettings::setChannelLoadThread(bool value)

View File

@ -29,26 +29,51 @@ Item
property bool is_contact: cntDt.md.is_contact property bool is_contact: cntDt.md.is_contact
property bool isOwn: cntDt.md.own property bool isOwn: cntDt.md.own
Button
Text
{ {
id: meText id: avatarPicker
text: "Yourself" text: "Change your Avatar"
visible: isOwn visible: isOwn
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 6
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
onClicked:
{
fileChooser.open()
}
CustomFileChooser
{
id: fileChooser
onResultFileChanged:
{
console.log("Result file changed! " , resultFile)
var base64Image = androidImagePicker.imageToBase64(resultFile)
rsApi.request("/identity/set_avatar", JSON.stringify({"gxs_id": cntDt.md.gxs_id, "avatar": base64Image }),
function (par)
{
var jP = JSON.parse(par.response)
if (jP.returncode === "ok")
{
console.log("Avatar changed! ")
topFace.getDetails()
}
})
}
}
} }
AvatarOrColorHash AvatarOrColorHash
{ {
id: topFace id: topFace
gxs_id: cntDt.md.gxs_id gxs_id: cntDt.md.gxs_id
anchors.top: meText.bottom anchors.top: (isOwn)? avatarPicker.bottom : parent.top
anchors.topMargin: 6 anchors.topMargin: 6
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
@ -82,22 +107,12 @@ Item
Image Image
{ {
source: source: cntDt.is_contact ?
{ "qrc:/icons/rating.svg" :
if (isOwn) "qrc:/icons/rating-unrated.svg"
{ height: parent.height -4
"qrc:/icons/keyring.svg"
}
else
{
cntDt.is_contact ?
"qrc:/icons/rating.svg" :
"qrc:/icons/rating-unrated.svg"
}
}
height: parent.height - 4
sourceSize.height: height
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
sourceSize.height: height
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
MouseArea MouseArea
@ -106,13 +121,10 @@ Item
onClicked: onClicked:
{ {
if (!isOwn) var jDt = JSON.stringify({gxs_id: cntDt.md.gxs_id})
{ if(cntDt.is_contact)
var jDt = JSON.stringify({gxs_id: cntDt.md.gxs_id}) rsApi.request("/identity/remove_contact", jDt, tgCt)
if(cntDt.is_contact) else rsApi.request("/identity/add_contact", jDt, tgCt)
rsApi.request("/identity/remove_contact", jDt, tgCt)
else rsApi.request("/identity/add_contact", jDt, tgCt)
}
} }
function tgCt() { cntDt.is_contact = !cntDt.is_contact } function tgCt() { cntDt.is_contact = !cntDt.is_contact }

View File

@ -207,4 +207,8 @@
<!-- Added by G10h4ck: Needed permission for autostart at boot --> <!-- Added by G10h4ck: Needed permission for autostart at boot -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- Added by Angesoc: used to pick images from gallery or take it from camera -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest> </manifest>

View File

@ -19,17 +19,25 @@
package org.retroshare.android.qml_app; package org.retroshare.android.qml_app;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log; import android.util.Log;
import android.net.Uri;
import org.qtproject.qt5.android.bindings.QtActivity; import org.qtproject.qt5.android.bindings.QtActivity;
import org.retroshare.android.qml_app.jni.NativeCalls; import org.retroshare.android.qml_app.jni.NativeCalls;
public class RetroShareQmlActivity extends QtActivity public class RetroShareQmlActivity extends QtActivity
{ {
static final int PICK_PHOTO = 1;
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
{ {
@ -84,4 +92,88 @@ public class RetroShareQmlActivity extends QtActivity
return true; return true;
return false; return false;
} }
private Uri capturedImageURI;
public void openImagePicker()
{
Log.i("RetroShareQmlActivity", "openImagePicker()");
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickIntent.setType("image/*");
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "Retroshare Avatar");
capturedImageURI = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePicture.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageURI);
Intent chooserIntent = Intent.createChooser(pickIntent, "Select Image");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {takePicture});
startActivityForResult( chooserIntent, PICK_PHOTO);
};
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.i("RetroShareQmlActivity", "onActivityResult()" + String.valueOf(requestCode));
if (resultCode == RESULT_OK)
{
if (requestCode == PICK_PHOTO)
{
final boolean isCamera;
if (data == null)
{
isCamera = true;
}
else
{
final String action = data.getAction();
if (action == null)
{
isCamera = false;
}
else
{
isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
}
}
Uri selectedImageUri;
if (isCamera)
{
selectedImageUri = capturedImageURI;
}
else
{
selectedImageUri = data == null ? null : data.getData();
}
String uri = getRealPathFromURI(selectedImageUri);
if (uri != null)
{
Log.i("RetroShareQmlActivity", "Image path from uri found!" + uri);
NativeCalls.notifyIntentUri("//file"+uri); // Add the authority for get it on qml code
}
}
}
}
public String getRealPathFromURI(Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
@SuppressWarnings("deprecation")
Cursor cursor = managedQuery(uri, projection, null, null, null);
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result = cursor.getString(column_index);
return result;
}
} }

View File

@ -0,0 +1,66 @@
#pragma once
#include <QObject>
#include <QDebug>
#include <QFile>
#include <QUrl>
#include <QImage>
#include <QImageReader>
#include <QBuffer>
#ifdef __ANDROID__
# include <QtAndroid>
# include <QtAndroidExtras/QAndroidJniObject>
#endif // __ANDROID__
struct AndroidImagePicker : QObject
{
Q_OBJECT
public slots:
static void openPicker()
{
qDebug() << "Starting image picker intent";
#ifdef __ANDROID__
QtAndroid::androidActivity().callMethod<void>(
"openImagePicker",
"()V" );
#endif // __ANDROID__
}
// Used to convert a given image path into a png base64 string
static QString imageToBase64 (QString const& path)
{
// Get local path from uri
QUrl url (path);
QString localPath = url.toLocalFile();
qDebug() << "imageToBase64() local path:" << localPath ;
// Read the image
QImageReader reader;
reader.setFileName(localPath);
QImage image = reader.read();
image = image.scaled(96,96,Qt::KeepAspectRatio,Qt::SmoothTransformation);
// Transform image into PNG format
QByteArray ba;
QBuffer buffer( &ba );
buffer.open( QIODevice::WriteOnly );
image.save( &buffer, "png" );
// Get Based 64 image string
QString encoded = QString(ba.toBase64());
qDebug() << "imageToBase64() encoded" ;
return encoded;
}
};

View File

@ -0,0 +1,60 @@
import QtQuick 2.7
import QtQuick.Dialogs 1.2
import "../URI.js" as UriJs
Item
{
id: compRoot
property var resultFile
FileDialog
{
id: fileDialog
title: "Please choose a file"
folder: shortcuts.pictures
nameFilters: [ "Image files (*.png *.jpg)"]
visible: false
selectMultiple: false
onAccepted: {
console.log("You chose: " + fileDialog.fileUrl)
resultFile = fileDialog.fileUrl
}
onRejected: {
console.log("Canceled")
}
}
function open()
{
if (Qt.platform.os === "android")
{
console.log("ImagePicker Android platform detected")
mainWindow.addUriHandler("file", androidResult)
androidImagePicker.openPicker()
}
else
{
fileDialog.visible = true
}
}
function androidResult (uri)
{
console.log("QML Android image uri found" , uri)
resultFile = normalizeUriToFilePath (uri)
mainWindow.delUriHandler("media", androidResult)
}
function normalizeUriToFilePath (uriStr)
{
var uri = new UriJs.URI(uriStr)
var hPath = uri.path()
return "file:///"+hPath
}
}

View File

@ -38,9 +38,9 @@
#include "libresapilocalclient.h" #include "libresapilocalclient.h"
#include "rsqmlappengine.h" #include "rsqmlappengine.h"
#include "androidimagepicker.h"
#include "platforminteracions.h" #include "platforminteracions.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@ -52,6 +52,7 @@ int main(int argc, char *argv[])
"org.retroshare.qml_components.LibresapiLocalClient", 1, 0, "org.retroshare.qml_components.LibresapiLocalClient", 1, 0,
"LibresapiLocalClient"); "LibresapiLocalClient");
QString sockPath = QDir::homePath() + "/.retroshare"; QString sockPath = QDir::homePath() + "/.retroshare";
sockPath.append("/libresapi.sock"); sockPath.append("/libresapi.sock");
@ -61,6 +62,14 @@ int main(int argc, char *argv[])
RsQmlAppEngine engine(true); RsQmlAppEngine engine(true);
QQmlContext& rootContext = *engine.rootContext(); QQmlContext& rootContext = *engine.rootContext();
qmlRegisterType<AndroidImagePicker>(
"org.retroshare.qml_components.AndroidImagePicker", 1, 0,
"AndroidImagePicker");
AndroidImagePicker androidImagePicker;
engine.rootContext()->setContextProperty("androidImagePicker",
&androidImagePicker);
QStringList mainArgs = app.arguments(); QStringList mainArgs = app.arguments();
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID

View File

@ -364,7 +364,7 @@ ApplicationWindow
function handleIntentUri(uriStr) function handleIntentUri(uriStr)
{ {
console.log("handleIntentUri(uriStr)") console.log("handleIntentUri(uriStr)", uriStr)
if(!Array.isArray(uriStr.match(/:\/\/[a-zA-Z.-]*\//g))) if(!Array.isArray(uriStr.match(/:\/\/[a-zA-Z.-]*\//g)))
{ {
@ -382,7 +382,10 @@ ApplicationWindow
var uri = new UriJs.URI(uriStr) var uri = new UriJs.URI(uriStr)
var hPath = uri.path() // no nesting ATM segmentCoded() var hPath = uri.path() // no nesting ATM segmentCoded()
console.log(hPath) console.log("hPath", hPath)
var authority = uri.authority()
console.log("authority", authority)
if(typeof uriHandlersRegister[hPath] == "function") if(typeof uriHandlersRegister[hPath] == "function")
{ {
@ -390,6 +393,13 @@ ApplicationWindow
hPath, uriHandlersRegister[hPath]) hPath, uriHandlersRegister[hPath])
uriHandlersRegister[hPath](uriStr) uriHandlersRegister[hPath](uriStr)
} }
else if (typeof uriHandlersRegister[authority] == "function" )
{
console.log("handleIntentUri(uriStr)", "found handler for path",
authority, uriHandlersRegister[authority])
uriHandlersRegister[authority](uriStr)
}
} }
function certificateLinkHandler(uriStr) function certificateLinkHandler(uriStr)

View File

@ -53,5 +53,6 @@
<file>components/emoji/emoji.js</file> <file>components/emoji/emoji.js</file>
<file>icons/network-connect.svg</file> <file>icons/network-connect.svg</file>
<file>icons/network-disconnect.svg</file> <file>icons/network-disconnect.svg</file>
<file>components/CustomFileChooser.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -6,6 +6,7 @@ CONFIG += c++11
HEADERS += libresapilocalclient.h \ HEADERS += libresapilocalclient.h \
rsqmlappengine.h \ rsqmlappengine.h \
androidimagepicker.h \
platforminteracions.h platforminteracions.h
SOURCES += main-app.cpp \ SOURCES += main-app.cpp \
libresapilocalclient.cpp \ libresapilocalclient.cpp \