diff --git a/.gitignore b/.gitignore index d680e7ad..89a17a3c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ router.keys i2p libi2pd.so netDb +/i2pd +/libi2pd.a +/libi2pdclient.a + # Autotools autom4te.cache diff --git a/ClientContext.cpp b/ClientContext.cpp index db1c8e6a..2bc13969 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -6,6 +6,7 @@ #include "FS.h" #include "Log.h" #include "Identity.h" +#include "util.h" #include "ClientContext.h" namespace i2p @@ -292,7 +293,7 @@ namespace client template std::string ClientContext::GetI2CPOption (const Section& section, const std::string& name, const Type& value) const { - return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); + return section.second.get (boost::property_tree::ptree::path_type (name, '/'), std::to_string (value)); } template diff --git a/Crypto.cpp b/Crypto.cpp index 90b74179..885c65f4 100644 --- a/Crypto.cpp +++ b/Crypto.cpp @@ -298,11 +298,13 @@ namespace crypto BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits #endif // calculate a - a = BN_new (); if (g_ElggTable) a = ElggPow (k, g_ElggTable, ctx); else + { + a = BN_new (); BN_mod_exp (a, elgg, k, elgp, ctx); + } BIGNUM * y = BN_new (); BN_bin2bn (key, 256, y); diff --git a/Daemon.cpp b/Daemon.cpp index 7ca28a6f..d34712a3 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -265,5 +265,5 @@ namespace i2p return true; } - } + } } diff --git a/Daemon.h b/Daemon.h index 977d9258..fa4f47ec 100644 --- a/Daemon.h +++ b/Daemon.h @@ -1,14 +1,9 @@ #ifndef DAEMON_H__ #define DAEMON_H__ +#include #include -#ifdef _WIN32 -#define Daemon i2p::util::DaemonWin32::Instance() -#else -#define Daemon i2p::util::DaemonLinux::Instance() -#endif - namespace i2p { namespace util @@ -36,7 +31,22 @@ namespace i2p Daemon_Singleton_Private &d; }; -#ifdef _WIN32 +#if defined(QT_GUI_LIB) // check if QT +#define Daemon i2p::util::DaemonQT::Instance() + // dummy, invoked from RunQT + class DaemonQT: public i2p::util::Daemon_Singleton + { + public: + + static DaemonQT& Instance() + { + static DaemonQT instance; + return instance; + } + }; + +#elif defined(_WIN32) +#define Daemon i2p::util::DaemonWin32::Instance() class DaemonWin32 : public Daemon_Singleton { public: @@ -52,7 +62,8 @@ namespace i2p void run (); }; #else - class DaemonLinux : public Daemon_Singleton +#define Daemon i2p::util::DaemonLinux::Instance() + class DaemonLinux : public Daemon_Singleton { public: static DaemonLinux& Instance() diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index 1cc0fa85..118fc5f5 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -74,7 +74,7 @@ namespace i2p } // point std{in,out,err} descriptors to /dev/null - stdin = freopen("/dev/null", "r", stdin); + stdin = freopen("/dev/null", "r", stdin); stdout = freopen("/dev/null", "w", stdout); stderr = freopen("/dev/null", "w", stderr); } diff --git a/FS.cpp b/FS.cpp index 380ab2e5..663c1916 100644 --- a/FS.cpp +++ b/FS.cpp @@ -55,6 +55,14 @@ namespace fs { dataDir += "/Library/Application Support/" + appName; return; #else /* other unix */ +#if defined(ANDROID) + if (boost::filesystem::exists("/sdcard")) + { + dataDir = "/sdcard/" + appName; + return; + } + // otherwise use /data/files +#endif char *home = getenv("HOME"); if (isService) { dataDir = "/var/lib/" + appName; diff --git a/HTTP.cpp b/HTTP.cpp index bb389356..57331bce 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -6,6 +6,7 @@ * See full license text in LICENSE file at top of project tree */ +#include "util.h" #include "HTTP.h" #include #include @@ -342,7 +343,7 @@ namespace http { if (status == "OK" && code != 200) status = HTTPCodeToStatus(code); // update if (body.length() > 0 && headers.count("Content-Length") == 0) - add_header("Content-Length", std::to_string(body.length()).c_str()); + add_header("Content-Length", std::to_string(body.length()).c_str()); /* build response */ std::stringstream ss; ss << version << " " << code << " " << status << CRLF; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 7b7b8bcc..b40cef27 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -21,6 +21,7 @@ #include "ClientContext.h" #include "HTTPServer.h" #include "Daemon.h" +#include "util.h" // For image and info #include "version.h" @@ -127,7 +128,7 @@ namespace http { "\r\n" /* TODO: Add support for locale */ " \r\n" " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ - " \r\n" + " \r\n" " Purple I2P " VERSION " Webconsole\r\n" << cssStyles << "\r\n"; @@ -136,15 +137,15 @@ namespace http { "
i2pd webconsole
\r\n" "
\r\n" "
\r\n" - " Main page
\r\n
\r\n" - " Router commands
\r\n" - " Local destinations
\r\n" - " Tunnels
\r\n" - " Transit tunnels
\r\n" - " Transports
\r\n" - " I2P tunnels
\r\n" - " Jump services
\r\n" - " SAM sessions
\r\n" + " Main page
\r\n
\r\n" + " Router commands
\r\n" + " Local destinations
\r\n" + " Tunnels
\r\n" + " Transit tunnels
\r\n" + " Transports
\r\n" + " I2P tunnels
\r\n" + " Jump services
\r\n" + " SAM sessions
\r\n" "
\r\n" "
"; } @@ -230,15 +231,15 @@ namespace http { clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); - s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; - s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n"; + s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; + s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n"; } void ShowJumpServices (std::stringstream& s, const std::string& address) { - s << "
"; + s << ""; s << ""; - s << ""; + s << ""; s << ""; s << "

\r\n"; s << "Jump services for " << address << "\r\n
    \r\n"; @@ -254,7 +255,7 @@ namespace http { for (auto& it: i2p::client::context.GetDestinations ()) { auto ident = it.second->GetIdentHash ();; - s << ""; + s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
    \r\n" << std::endl; } } @@ -358,21 +359,21 @@ namespace http { { /* commands */ s << "Router Commands
    \r\n"; - s << " Run peer test
    \r\n"; - //s << " Reload config
    \r\n"; + s << " Run peer test
    \r\n"; + //s << " Reload config
    \r\n"; if (i2p::context.AcceptsTunnels ()) - s << " Stop accepting tunnels
    \r\n"; + s << " Stop accepting tunnels
    \r\n"; else - s << " Start accepting tunnels
    \r\n"; -#ifndef WIN32 + s << " Start accepting tunnels
    \r\n"; +#if (!defined(WIN32) && !defined(QT_GUI_LIB)) if (Daemon.gracefullShutdownInterval) { - s << " Cancel gracefull shutdown ("; + s << " Cancel gracefull shutdown ("; s << Daemon.gracefullShutdownInterval; s << " seconds remains)
    \r\n"; } else { - s << " Start gracefull shutdown
    \r\n"; + s << " Start gracefull shutdown
    \r\n"; } - s << " Force shutdown
    \r\n"; + s << " Force shutdown
    \r\n"; #endif } @@ -450,7 +451,7 @@ namespace http { s << "SAM Sessions:
    \r\n
    \r\n"; for (auto& it: sam->GetSessions ()) { - s << ""; + s << ""; s << it.first << "
    \r\n" << std::endl; } } @@ -469,7 +470,7 @@ namespace http { return; } auto& ident = session->localDestination->GetIdentHash(); - s << ""; + s << ""; s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
    \r\n"; s << "
    \r\n"; s << "Streams:
    \r\n"; @@ -493,7 +494,7 @@ namespace http { for (auto& it: i2p::client::context.GetClientTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << ""; + s << ""; s << it.second->GetName () << " ⇐ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
    \r\n"<< std::endl; @@ -502,7 +503,7 @@ namespace http { for (auto& it: i2p::client::context.GetServerTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << ""; + s << ""; s << it.second->GetName () << " ⇒ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << ":" << it.second->GetLocalPort (); @@ -611,7 +612,7 @@ namespace http { HandleCommand (req, res, s); } else { ShowStatus (s); - res.add_header("Refresh", "5"); + //res.add_header("Refresh", "5"); } ShowPageTail (s); @@ -677,12 +678,12 @@ namespace http { i2p::context.SetAcceptsTunnels (false); else if (cmd == HTTP_COMMAND_SHUTDOWN_START) { i2p::context.SetAcceptsTunnels (false); -#ifndef WIN32 +#if (!defined(WIN32) && !defined(QT_GUI_LIB)) Daemon.gracefullShutdownInterval = 10*60; #endif } else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) { i2p::context.SetAcceptsTunnels (true); -#ifndef WIN32 +#if (!defined(WIN32) && !defined(QT_GUI_LIB)) Daemon.gracefullShutdownInterval = 0; #endif } else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) { diff --git a/I2CP.cpp b/I2CP.cpp index dab7f9b0..f506a312 100644 --- a/I2CP.cpp +++ b/I2CP.cpp @@ -115,15 +115,14 @@ namespace client } I2CPSession::I2CPSession (I2CPServer& owner, std::shared_ptr socket): - m_Owner (owner), m_Socket (socket), - m_NextMessage (nullptr), m_NextMessageLen (0), m_NextMessageOffset (0), + m_Owner (owner), m_Socket (socket), m_Payload (nullptr), m_SessionID (0xFFFF), m_MessageID (0), m_IsSendAccepted (true) { } I2CPSession::~I2CPSession () { - delete[] m_NextMessage; + delete[] m_Payload; } void I2CPSession::Start () @@ -141,117 +140,72 @@ namespace client if (m_Socket) { auto s = shared_from_this (); - m_Socket->async_read_some (boost::asio::buffer (m_Buffer, 1), + m_Socket->async_read_some (boost::asio::buffer (m_Header, 1), [s](const boost::system::error_code& ecode, std::size_t bytes_transferred) { - if (!ecode && bytes_transferred > 0 && s->m_Buffer[0] == I2CP_PROTOCOL_BYTE) - s->Receive (); + if (!ecode && bytes_transferred > 0 && s->m_Header[0] == I2CP_PROTOCOL_BYTE) + s->ReceiveHeader (); else s->Terminate (); }); } } - void I2CPSession::Receive () + void I2CPSession::ReceiveHeader () { - m_Socket->async_read_some (boost::asio::buffer (m_Buffer, I2CP_SESSION_BUFFER_SIZE), - std::bind (&I2CPSession::HandleReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Header, I2CP_HEADER_SIZE), + boost::asio::transfer_all (), + std::bind (&I2CPSession::HandleReceivedHeader, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } - void I2CPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + void I2CPSession::HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) Terminate (); else { - size_t offset = 0; // from m_Buffer - if (m_NextMessage) + m_PayloadLen = bufbe32toh (m_Header + I2CP_HEADER_LENGTH_OFFSET); + if (m_PayloadLen > 0) { - if (!m_NextMessageLen) // we didn't receive header yet - { - if (m_NextMessageOffset + bytes_transferred < I2CP_HEADER_SIZE) - { - // still no complete header - memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, bytes_transferred); - m_NextMessageOffset += bytes_transferred; - offset = bytes_transferred; - } - else - { - // we know message length now - offset = I2CP_HEADER_SIZE - m_NextMessageOffset; - memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer, offset); - m_NextMessageLen = bufbe32toh (m_NextMessage + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE; - m_NextMessageOffset = I2CP_HEADER_SIZE; - } - } - - if (offset < bytes_transferred) - { - auto msgRemainingLen = m_NextMessageLen - m_NextMessageOffset; - auto bufRemainingLen = bytes_transferred - offset; - if (bufRemainingLen < msgRemainingLen) - { - memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer + offset, bufRemainingLen); - m_NextMessageOffset += bufRemainingLen; - offset += bufRemainingLen; - } - else - { - // m_NextMessage complete - offset += msgRemainingLen; - memcpy (m_NextMessage + m_NextMessageOffset, m_Buffer + offset, msgRemainingLen); - HandleNextMessage (m_NextMessage); - delete[] m_NextMessage; - m_NextMessage = nullptr; - } - } - } - // process the rest - while (offset < bytes_transferred) + m_Payload = new uint8_t[m_PayloadLen]; + ReceivePayload (); + } + else // no following payload { - if (bytes_transferred - offset < I2CP_HEADER_SIZE) - { - // we don't have message header yet - m_NextMessage = new uint8_t[0xFFFF]; // allocate 64K - m_NextMessageLen = 0; // we must set message length later - m_NextMessageOffset = bytes_transferred - offset; - memcpy (m_NextMessage, m_Buffer + offset, m_NextMessageOffset); // just copy it - break; - } - - auto msgLen = bufbe32toh (m_Buffer + offset + I2CP_HEADER_LENGTH_OFFSET) + I2CP_HEADER_SIZE; - if (msgLen > 0xFFFF) // 64K - { - LogPrint (eLogError, "I2CP: message length ", msgLen, " exceeds 64K. Terminated"); - Terminate (); - return; - } - if (msgLen <= bytes_transferred - offset) - { - HandleNextMessage (m_Buffer + offset); - offset += msgLen; - } - else - { - m_NextMessageLen = msgLen; - m_NextMessageOffset = bytes_transferred - offset; - m_NextMessage = new uint8_t[m_NextMessageLen]; - memcpy (m_NextMessage, m_Buffer + offset, m_NextMessageOffset); - offset = bytes_transferred; - } - } - Receive (); + HandleMessage (); + ReceiveHeader (); // next message + } + } + } + + void I2CPSession::ReceivePayload () + { + boost::asio::async_read (*m_Socket, boost::asio::buffer (m_Payload, m_PayloadLen), + boost::asio::transfer_all (), + std::bind (&I2CPSession::HandleReceivedPayload, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); + } + + void I2CPSession::HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred) + { + if (ecode) + Terminate (); + else + { + HandleMessage (); + delete[] m_Payload; + m_Payload = nullptr; + m_PayloadLen = 0; + ReceiveHeader (); // next message } } - void I2CPSession::HandleNextMessage (const uint8_t * buf) + void I2CPSession::HandleMessage () { - auto handler = m_Owner.GetMessagesHandlers ()[buf[I2CP_HEADER_TYPE_OFFSET]]; + auto handler = m_Owner.GetMessagesHandlers ()[m_Header[I2CP_HEADER_TYPE_OFFSET]]; if (handler) - (this->*handler)(buf + I2CP_HEADER_SIZE, bufbe32toh (buf + I2CP_HEADER_LENGTH_OFFSET)); + (this->*handler)(m_Payload, m_PayloadLen); else - LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)buf[I2CP_HEADER_TYPE_OFFSET]); + LogPrint (eLogError, "I2CP: Unknown I2CP messsage ", (int)m_Header[I2CP_HEADER_TYPE_OFFSET]); } void I2CPSession::Terminate () @@ -267,6 +221,7 @@ namespace client m_Socket = nullptr; } m_Owner.RemoveSession (GetSessionID ()); + LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " terminated"); } void I2CPSession::SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len) diff --git a/I2CP.h b/I2CP.h index f77607ba..6fc0e846 100644 --- a/I2CP.h +++ b/I2CP.h @@ -126,9 +126,11 @@ namespace client private: void ReadProtocolByte (); - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleNextMessage (const uint8_t * buf); + void ReceiveHeader (); + void HandleReceivedHeader (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void ReceivePayload (); + void HandleReceivedPayload (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleMessage (); void Terminate (); void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, const uint8_t * buf); @@ -143,8 +145,8 @@ namespace client I2CPServer& m_Owner; std::shared_ptr m_Socket; - uint8_t m_Buffer[I2CP_SESSION_BUFFER_SIZE], * m_NextMessage; - size_t m_NextMessageLen, m_NextMessageOffset; + uint8_t m_Header[I2CP_HEADER_SIZE], * m_Payload; + size_t m_PayloadLen; std::shared_ptr m_Destination; uint16_t m_SessionID; diff --git a/I2PControl.cpp b/I2PControl.cpp index c87db150..c0c87fd4 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -23,6 +23,7 @@ #include "Timestamp.h" #include "Transports.h" #include "version.h" +#include "util.h" #include "I2PControl.h" namespace i2p @@ -315,7 +316,7 @@ namespace client } InsertParam (results, "API", api); results << ","; - std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ()); + std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ()); m_Tokens.insert (token); InsertParam (results, "Token", token); } diff --git a/README.md b/README.md index b985abf4..f5ce9624 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,55 @@ i2pd ==== -Independent C++ implementation of I2P router +i2pd is a full-featured C++ implementation of +[I2P](https://geti2p.net/en/about/intro) client. -License -------- +I2P (Invisible Internet Project) is anonymous network which works on top of +public Internet. Privacy and anonymity are achieved by strong encryption and +bouncing your traffic through thousands of I2P nodes all around the world. -This project is licensed under the BSD 3-clause license, which can be found in the file -LICENSE in the root of the project source code. +We are building network which helps people to communicate and share information +without restrictions. -Donations ---------- +* [Website](http://i2pd.website) +* [Documentation](https://i2pd.readthedocs.io/en/latest/) +* [Wiki](https://github.com/PurpleI2P/i2pd/wiki) +* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues) +* [Twitter](https://twitter.com/i2porignal) -BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY -LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59 -ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z -DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y +Installing +---------- -Documentation: --------------- -http://i2pd.readthedocs.org +The easiest way to install i2pd is by using +[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest). +See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build +i2pd from source on your OS. -Supported OS ------------- +**Supported systems:** * Linux x86/x64 - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) * Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) * Mac OS X * FreeBSD +* Android + +Using i2pd +---------- + +See [documentation](https://i2pd.readthedocs.io/en/latest/) and +[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf). -More documentation ------------------- +Donations +--------- + +BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY +DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF +LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59 +ANC: AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z +DOGE: DNXLQKziRPAsD9H3DFNjk4fLQrdaSX893Y + +License +------- -* [Building from source / unix](docs/build_notes_unix.md) -* [Building from source / windows](docs/build_notes_windows.md) -* [Configuring your i2pd](docs/configuration.md) -* [Github wiki](https://github.com/PurpleI2P/i2pd/wiki/) +This project is licensed under the BSD 3-clause license, which can be found in the file +LICENSE in the root of the project source code. diff --git a/Reseed.cpp b/Reseed.cpp index 6f27891d..722d7eff 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -16,7 +16,6 @@ #include "NetDb.h" #include "util.h" - namespace i2p { namespace data @@ -373,7 +372,7 @@ namespace data boost::asio::io_service service; boost::system::error_code ecode; auto it = boost::asio::ip::tcp::resolver(service).resolve ( - boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode); + boost::asio::ip::tcp::resolver::query (u.host_, std::to_string (u.port_)), ecode); if (!ecode) { boost::asio::ssl::context ctx(service, boost::asio::ssl::context::sslv23); diff --git a/RouterContext.cpp b/RouterContext.cpp index 5fa32a13..768750bb 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -56,7 +56,7 @@ namespace i2p routerInfo.AddNTCPAddress (host.c_str(), port); routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC - routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID)); + routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID)); routerInfo.SetProperty ("router.version", I2P_VERSION); routerInfo.CreateBuffer (m_Keys); m_RouterInfo.SetRouterIdentity (GetIdentity ()); diff --git a/Transports.cpp b/Transports.cpp index 2d3d423f..b15ec56f 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -456,8 +456,13 @@ namespace transport { m_SSUServer->DeleteSession (ssuSession); LogPrint (eLogDebug, "Transports: SSU session closed"); - } - // TODO: delete NTCP + } + auto ntcpSession = m_NTCPServer ? m_NTCPServer->FindNTCPSession(router->GetIdentHash()) : nullptr; + if (ntcpSession) // try deleting ntcp session too + { + ntcpSession->Terminate (); + LogPrint(eLogDebug, "Transports: NTCP session closed"); + } } void Transports::DetectExternalIP () diff --git a/Tunnel.cpp b/Tunnel.cpp index 1403330b..961cc97e 100644 --- a/Tunnel.cpp +++ b/Tunnel.cpp @@ -861,6 +861,12 @@ namespace tunnel // TODO: locking return m_OutboundTunnels.size(); } + +#ifdef ANDROID_ARM7A + template std::shared_ptr Tunnels::CreateTunnel(std::shared_ptr, std::shared_ptr); + template std::shared_ptr Tunnels::CreateTunnel(std::shared_ptr, std::shared_ptr); +#endif + } } diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 00000000..79c3f959 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,10 @@ +# Various generated files +/CMakeFiles/ +/i2pd +/libi2pd.a +/libi2pdclient.a +/cmake_install.cmake +/CMakeCache.txt +/CPackConfig.cmake +/CPackSourceConfig.cmake +/install_manifest.txt diff --git a/docs/index.rst b/docs/index.rst index 3963dc3c..d0471add 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,20 +1,27 @@ i2pd ==== -`Website `_ | -`Github `_ | -`Issues `_ +i2pd is a full-featured C++ implementation of +`I2P `_ client. -i2pd is C++ implementation of `I2P `_. +* `Website `_ +* `GitHub `_ +* `Wiki `_ +* `Tickets/Issues `_ +* `Twitter `_ -Supports: ---------- - -* Complete I2P router functionality -* Floodfill -* HTTP and SOCKS proxy -* I2P client and server tunnels -* SAM and BOB interfaces +Installing +---------- + +The easiest way to install i2pd is by using +`precompiled binaries `_. +See documentation for how to build i2pd from source on your OS. + +Using i2pd +---------- + +See documentation and +`example config file `_. Contents: --------- @@ -28,3 +35,4 @@ Contents: configuration family + diff --git a/i2pd.cpp b/i2pd.cpp index f3ac6b3f..589274f5 100644 --- a/i2pd.cpp +++ b/i2pd.cpp @@ -1,9 +1,24 @@ #include #include "Daemon.h" +#if defined(QT_GUI_LIB) + +namespace i2p +{ +namespace qt +{ + int RunQT (int argc, char* argv[]); +} +} int main( int argc, char* argv[] ) { - if (Daemon.init(argc, argv)) + return i2p::qt::RunQT (argc, argv); +} + +#else +int main( int argc, char* argv[] ) +{ + if (Daemon.init(argc, argv)) { if (Daemon.start()) Daemon.run (); @@ -11,6 +26,7 @@ int main( int argc, char* argv[] ) } return EXIT_SUCCESS; } +#endif #ifdef _WIN32 #include diff --git a/qt/.gitignore b/qt/.gitignore new file mode 100644 index 00000000..a0155cb2 --- /dev/null +++ b/qt/.gitignore @@ -0,0 +1 @@ +/build*/ diff --git a/qt/i2pd_qt/DaemonQT.cpp b/qt/i2pd_qt/DaemonQT.cpp new file mode 100644 index 00000000..5cd43858 --- /dev/null +++ b/qt/i2pd_qt/DaemonQT.cpp @@ -0,0 +1,161 @@ +#include "DaemonQT.h" +#include "../../Daemon.h" +#include "mainwindow.h" +#include +#include +#include +#include + +namespace i2p +{ +namespace qt +{ + Worker::Worker (DaemonQTImpl& daemon): + m_Daemon (daemon) + { + } + + void Worker::startDaemon() + { + qDebug("Performing daemon start..."); + m_Daemon.start(); + qDebug("Daemon started."); + emit resultReady(); + } + void Worker::restartDaemon() + { + qDebug("Performing daemon restart..."); + m_Daemon.restart(); + qDebug("Daemon restarted."); + emit resultReady(); + } + void Worker::stopDaemon() { + qDebug("Performing daemon stop..."); + m_Daemon.stop(); + qDebug("Daemon stopped."); + emit resultReady(); + } + + Controller::Controller(DaemonQTImpl& daemon): + m_Daemon (daemon) + { + Worker *worker = new Worker (m_Daemon); + worker->moveToThread(&workerThread); + connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &Controller::startDaemon, worker, &Worker::startDaemon); + connect(this, &Controller::stopDaemon, worker, &Worker::stopDaemon); + connect(this, &Controller::restartDaemon, worker, &Worker::restartDaemon); + connect(worker, &Worker::resultReady, this, &Controller::handleResults); + workerThread.start(); + } + Controller::~Controller() + { + qDebug("Closing and waiting for daemon worker thread..."); + workerThread.quit(); + workerThread.wait(); + qDebug("Waiting for daemon worker thread finished."); + if(m_Daemon.isRunning()) + { + qDebug("Stopping the daemon..."); + m_Daemon.stop(); + qDebug("Stopped the daemon."); + } + } + + DaemonQTImpl::DaemonQTImpl (): + mutex(nullptr), m_IsRunning(nullptr), m_RunningChangedCallback(nullptr) + { + } + + DaemonQTImpl::~DaemonQTImpl () + { + delete mutex; + } + + bool DaemonQTImpl::init(int argc, char* argv[]) + { + mutex=new QMutex(QMutex::Recursive); + setRunningCallback(0); + m_IsRunning=false; + return Daemon.init(argc,argv); + } + + void DaemonQTImpl::start() + { + QMutexLocker locker(mutex); + setRunning(true); + Daemon.start(); + } + + void DaemonQTImpl::stop() + { + QMutexLocker locker(mutex); + Daemon.stop(); + setRunning(false); + } + + void DaemonQTImpl::restart() + { + QMutexLocker locker(mutex); + stop(); + start(); + } + + void DaemonQTImpl::setRunningCallback(runningChangedCallback cb) + { + m_RunningChangedCallback = cb; + } + + bool DaemonQTImpl::isRunning() + { + return m_IsRunning; + } + + void DaemonQTImpl::setRunning(bool newValue) + { + bool oldValue = m_IsRunning; + if(oldValue!=newValue) + { + m_IsRunning = newValue; + if(m_RunningChangedCallback) + m_RunningChangedCallback(); + } + } + + int RunQT (int argc, char* argv[]) + { + QApplication app(argc, argv); + int result; + + { + DaemonQTImpl daemon; + qDebug("Initialising the daemon..."); + bool daemonInitSuccess = daemon.init(argc, argv); + if(!daemonInitSuccess) + { + QMessageBox::critical(0, "Error", "Daemon init failed"); + return 1; + } + qDebug("Initialised, creating the main window..."); + MainWindow w; + qDebug("Before main window.show()..."); + w.show (); + + { + i2p::qt::Controller daemonQtController(daemon); + qDebug("Starting the daemon..."); + emit daemonQtController.startDaemon(); + //daemon.start (); + qDebug("Starting GUI event loop..."); + result = app.exec(); + //daemon.stop (); + } + } + + //QMessageBox::information(&w, "Debug", "demon stopped"); + qDebug("Exiting the application"); + return result; + } +} +} + diff --git a/qt/i2pd_qt/DaemonQT.h b/qt/i2pd_qt/DaemonQT.h new file mode 100644 index 00000000..98e8b4e6 --- /dev/null +++ b/qt/i2pd_qt/DaemonQT.h @@ -0,0 +1,81 @@ +#ifndef DAEMONQT_H +#define DAEMONQT_H + +#include +#include +#include + +namespace i2p +{ +namespace qt +{ + class DaemonQTImpl + { + public: + + DaemonQTImpl (); + ~DaemonQTImpl (); + + typedef void (*runningChangedCallback)(); + + /** + * @brief init + * @param argc + * @param argv + * @return success + */ + bool init(int argc, char* argv[]); + void start(); + void stop(); + void restart(); + void setRunningCallback(runningChangedCallback cb); + bool isRunning(); + private: + void setRunning(bool running); + private: + QMutex* mutex; + bool m_IsRunning; + runningChangedCallback m_RunningChangedCallback; + }; + + class Worker : public QObject + { + Q_OBJECT + public: + + Worker (DaemonQTImpl& daemon); + + private: + + DaemonQTImpl& m_Daemon; + + public slots: + void startDaemon(); + void restartDaemon(); + void stopDaemon(); + + signals: + void resultReady(); + }; + + class Controller : public QObject + { + Q_OBJECT + QThread workerThread; + public: + Controller(DaemonQTImpl& daemon); + ~Controller(); + private: + DaemonQTImpl& m_Daemon; + + public slots: + void handleResults(){} + signals: + void startDaemon(); + void stopDaemon(); + void restartDaemon(); + }; +} +} + +#endif // DAEMONQT_H diff --git a/qt/i2pd_qt/android/.gitignore b/qt/i2pd_qt/android/.gitignore new file mode 100644 index 00000000..2c41ac3e --- /dev/null +++ b/qt/i2pd_qt/android/.gitignore @@ -0,0 +1 @@ +/gen/ diff --git a/qt/i2pd_qt/android/AndroidManifest.xml b/qt/i2pd_qt/android/AndroidManifest.xml new file mode 100644 index 00000000..6ab763ff --- /dev/null +++ b/qt/i2pd_qt/android/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qt/i2pd_qt/android/build.gradle b/qt/i2pd_qt/android/build.gradle new file mode 100644 index 00000000..ef416b0b --- /dev/null +++ b/qt/i2pd_qt/android/build.gradle @@ -0,0 +1,57 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.1.0' + } +} + +allprojects { + repositories { + jcenter() + } +} + +apply plugin: 'com.android.application' + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion androidBuildToolsVersion + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + lintOptions { + abortOnError false + } +} diff --git a/qt/i2pd_qt/android/libs/android-support-v4.jar b/qt/i2pd_qt/android/libs/android-support-v4.jar new file mode 100644 index 00000000..2ff47f4f Binary files /dev/null and b/qt/i2pd_qt/android/libs/android-support-v4.jar differ diff --git a/qt/i2pd_qt/android/project.properties b/qt/i2pd_qt/android/project.properties new file mode 100644 index 00000000..4d07452b --- /dev/null +++ b/qt/i2pd_qt/android/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-11 diff --git a/qt/i2pd_qt/android/res/drawable-hdpi/icon.png b/qt/i2pd_qt/android/res/drawable-hdpi/icon.png new file mode 100644 index 00000000..a5dc7b68 Binary files /dev/null and b/qt/i2pd_qt/android/res/drawable-hdpi/icon.png differ diff --git a/qt/i2pd_qt/android/res/drawable/itoopie_notification_icon.png b/qt/i2pd_qt/android/res/drawable/itoopie_notification_icon.png new file mode 100644 index 00000000..8fbe2468 Binary files /dev/null and b/qt/i2pd_qt/android/res/drawable/itoopie_notification_icon.png differ diff --git a/qt/i2pd_qt/android/res/layout/splash.xml b/qt/i2pd_qt/android/res/layout/splash.xml new file mode 100644 index 00000000..476d91a8 --- /dev/null +++ b/qt/i2pd_qt/android/res/layout/splash.xml @@ -0,0 +1,4 @@ + + diff --git a/qt/i2pd_qt/android/res/values-de/strings.xml b/qt/i2pd_qt/android/res/values-de/strings.xml new file mode 100644 index 00000000..320d9ec3 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-de/strings.xml @@ -0,0 +1,6 @@ + + + Ministro-Dienst wurde nicht gefunden.\nAnwendung kann nicht gestartet werden + Diese Anwendung benötigt den Ministro-Dienst. Möchten Sie ihn installieren? + In Ihrer Anwendung ist ein schwerwiegender Fehler aufgetreten, sie kann nicht fortgesetzt werden + diff --git a/qt/i2pd_qt/android/res/values-el/strings.xml b/qt/i2pd_qt/android/res/values-el/strings.xml new file mode 100644 index 00000000..3cab212f --- /dev/null +++ b/qt/i2pd_qt/android/res/values-el/strings.xml @@ -0,0 +1,6 @@ + + + Δεν ήταν δυνατή η εύρεση της υπηρεσίας Ministro. Δεν είναι δυνατή η εκκίνηση της εφαρμογής. + Η εφαρμογή απαιτεί την υπηρεσία Ministro. Να εγκατασταθεί η υπηρεσία? + Παρουσιάστηκε ένα κρίσιμο σφάλμα και η εφαρμογή δεν μπορεί να συνεχίσει. + diff --git a/qt/i2pd_qt/android/res/values-es/strings.xml b/qt/i2pd_qt/android/res/values-es/strings.xml new file mode 100644 index 00000000..cf0b54d0 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-es/strings.xml @@ -0,0 +1,6 @@ + + + Servicio Ministro inesistente. Imposible ejecutar la aplicación. + Esta aplicación requiere el servicio Ministro. Instalarlo? + La aplicación ha causado un error grave y no es posible continuar. + diff --git a/qt/i2pd_qt/android/res/values-et/strings.xml b/qt/i2pd_qt/android/res/values-et/strings.xml new file mode 100644 index 00000000..d55a3c14 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-et/strings.xml @@ -0,0 +1,6 @@ + + + Ei suuda leida Ministro teenust.\nProgrammi ei saa käivitada. + See programm vajab Ministro teenust.\nKas soovite paigaldada? + Programmiga juhtus fataalne viga.\nKahjuks ei saa jätkata. + diff --git a/qt/i2pd_qt/android/res/values-fa/strings.xml b/qt/i2pd_qt/android/res/values-fa/strings.xml new file mode 100644 index 00000000..a8d1b874 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-fa/strings.xml @@ -0,0 +1,6 @@ + + + سرویس Ministro را پیدا نمی‌کند. برنامه نمی‌تواند آغاز شود. + این نرم‌افزار به سرویس Ministro احتیاج دارد. آیا دوست دارید آن را نصب کنید؟ + خطایی اساسی در برنامه‌تان رخ داد و اجرای برنامه نمی‌تواند ادامه یابد. + diff --git a/qt/i2pd_qt/android/res/values-fr/strings.xml b/qt/i2pd_qt/android/res/values-fr/strings.xml new file mode 100644 index 00000000..efc0fb6e --- /dev/null +++ b/qt/i2pd_qt/android/res/values-fr/strings.xml @@ -0,0 +1,6 @@ + + + Le service Ministro est introuvable.\nL\'application ne peut pas démarrer. + Cette application requiert le service Ministro. Voulez-vous l\'installer? + Votre application a rencontré une erreur fatale et ne peut pas continuer. + diff --git a/qt/i2pd_qt/android/res/values-id/strings.xml b/qt/i2pd_qt/android/res/values-id/strings.xml new file mode 100644 index 00000000..aaa5bda0 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-id/strings.xml @@ -0,0 +1,6 @@ + + + Layanan Ministro tidak bisa ditemukan.\nAplikasi tidak bisa dimulai. + Aplikasi ini membutuhkan layanan Ministro. Apakah Anda ingin menginstalnya? + Aplikasi Anda mengalami kesalahan fatal dan tidak dapat melanjutkan. + diff --git a/qt/i2pd_qt/android/res/values-it/strings.xml b/qt/i2pd_qt/android/res/values-it/strings.xml new file mode 100644 index 00000000..4773419c --- /dev/null +++ b/qt/i2pd_qt/android/res/values-it/strings.xml @@ -0,0 +1,6 @@ + + + Servizio Ministro inesistente. Impossibile eseguire \nl\'applicazione. + Questa applicazione richiede il servizio Ministro.Installarlo? + L\'applicazione ha provocato un errore grave e non puo\' continuare. + diff --git a/qt/i2pd_qt/android/res/values-ja/strings.xml b/qt/i2pd_qt/android/res/values-ja/strings.xml new file mode 100644 index 00000000..ba1cfda9 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-ja/strings.xml @@ -0,0 +1,6 @@ + + + Ministroサービスが見つかりません。\nアプリケーションが起動できません。 + このアプリケーションにはMinistroサービスが必要です。 インストールしてもよろしいですか? + アプリケーションで致命的なエラーが発生したため続行できません。 + diff --git a/qt/i2pd_qt/android/res/values-ms/strings.xml b/qt/i2pd_qt/android/res/values-ms/strings.xml new file mode 100644 index 00000000..6e3952ea --- /dev/null +++ b/qt/i2pd_qt/android/res/values-ms/strings.xml @@ -0,0 +1,6 @@ + + + Tidak jumpa servis Ministro.\nAplikasi tidak boleh dimulakan. + Aplikasi ini memerlukan servis Ministro. Adakah anda ingin pasang servis itu? + Aplikasi anda menemui ralat muat dan tidak boleh diteruskan. + diff --git a/qt/i2pd_qt/android/res/values-nb/strings.xml b/qt/i2pd_qt/android/res/values-nb/strings.xml new file mode 100644 index 00000000..8a550e99 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-nb/strings.xml @@ -0,0 +1,6 @@ + + + Kan ikke finne tjenesten Ministro. Applikasjonen kan ikke starte. + Denne applikasjonen krever tjenesten Ministro. Vil du installere denne? + Applikasjonen fikk en kritisk feil og kan ikke fortsette + diff --git a/qt/i2pd_qt/android/res/values-nl/strings.xml b/qt/i2pd_qt/android/res/values-nl/strings.xml new file mode 100644 index 00000000..8a45a724 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-nl/strings.xml @@ -0,0 +1,6 @@ + + + De Ministro service is niet gevonden.\nDe applicatie kan niet starten. + Deze applicatie maakt gebruik van de Ministro service. Wilt u deze installeren? + Er is een fatale fout in de applicatie opgetreden. De applicatie kan niet verder gaan. + diff --git a/qt/i2pd_qt/android/res/values-pl/strings.xml b/qt/i2pd_qt/android/res/values-pl/strings.xml new file mode 100644 index 00000000..9fefc92d --- /dev/null +++ b/qt/i2pd_qt/android/res/values-pl/strings.xml @@ -0,0 +1,6 @@ + + + Usługa Ministro nie została znaleziona.\nAplikacja nie może zostać uruchomiona. + Aplikacja wymaga usługi Ministro. Czy chcesz ją zainstalować? + Wystąpił błąd krytyczny. Aplikacja zostanie zamknięta. + diff --git a/qt/i2pd_qt/android/res/values-pt-rBR/strings.xml b/qt/i2pd_qt/android/res/values-pt-rBR/strings.xml new file mode 100644 index 00000000..67ac3f9f --- /dev/null +++ b/qt/i2pd_qt/android/res/values-pt-rBR/strings.xml @@ -0,0 +1,6 @@ + + + Não foi possível encontrar o serviço Ministro.\nA aplicação não pode iniciar. + Essa aplicação requer o serviço Ministro. Gostaria de instalá-lo? + Sua aplicação encontrou um erro fatal e não pode continuar. + diff --git a/qt/i2pd_qt/android/res/values-ro/strings.xml b/qt/i2pd_qt/android/res/values-ro/strings.xml new file mode 100644 index 00000000..f88a442b --- /dev/null +++ b/qt/i2pd_qt/android/res/values-ro/strings.xml @@ -0,0 +1,6 @@ + + + Serviciul Ministro nu poate fi găsit.\nAplicaţia nu poate porni. + Această aplicaţie necesită serviciul Ministro.\nDoriţi să-l instalaţi? + Aplicaţia dumneavoastră a întâmpinat o eroare fatală şi nu poate continua. + diff --git a/qt/i2pd_qt/android/res/values-rs/strings.xml b/qt/i2pd_qt/android/res/values-rs/strings.xml new file mode 100644 index 00000000..3194ce90 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-rs/strings.xml @@ -0,0 +1,6 @@ + + + Ministro servise nije pronađen. Aplikacija ne može biti pokrenuta. + Ova aplikacija zahteva Ministro servis. Želite li da ga instalirate? + Vaša aplikacija je naišla na fatalnu grešku i ne može nastaviti sa radom. + diff --git a/qt/i2pd_qt/android/res/values-ru/strings.xml b/qt/i2pd_qt/android/res/values-ru/strings.xml new file mode 100644 index 00000000..d3cee80f --- /dev/null +++ b/qt/i2pd_qt/android/res/values-ru/strings.xml @@ -0,0 +1,6 @@ + + + Сервис Ministro не найден.\nПриложение нельзя запустить. + Этому приложению необходим сервис Ministro. Вы хотите его установить? + Ваше приложение столкнулось с фатальной ошибкой и не может более работать. + diff --git a/qt/i2pd_qt/android/res/values-zh-rCN/strings.xml b/qt/i2pd_qt/android/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..2eb12698 --- /dev/null +++ b/qt/i2pd_qt/android/res/values-zh-rCN/strings.xml @@ -0,0 +1,6 @@ + + + 无法找到Ministro服务。\n应用程序无法启动。 + 此应用程序需要Ministro服务。您想安装它吗? + 您的应用程序遇到一个致命错误导致它无法继续。 + diff --git a/qt/i2pd_qt/android/res/values-zh-rTW/strings.xml b/qt/i2pd_qt/android/res/values-zh-rTW/strings.xml new file mode 100644 index 00000000..f6e68efa --- /dev/null +++ b/qt/i2pd_qt/android/res/values-zh-rTW/strings.xml @@ -0,0 +1,6 @@ + + + 無法找到Ministro服務。\n應用程序無法啟動。 + 此應用程序需要Ministro服務。您想安裝它嗎? + 您的應用程序遇到一個致命錯誤導致它無法繼續。 + diff --git a/qt/i2pd_qt/android/res/values/libs.xml b/qt/i2pd_qt/android/res/values/libs.xml new file mode 100644 index 00000000..4d68673c --- /dev/null +++ b/qt/i2pd_qt/android/res/values/libs.xml @@ -0,0 +1,25 @@ + + + + https://download.qt-project.org/ministro/android/qt5/qt-5.4 + + + + + + + + + + + + + + + + + + + + diff --git a/qt/i2pd_qt/android/res/values/strings.xml b/qt/i2pd_qt/android/res/values/strings.xml new file mode 100644 index 00000000..713c7aa0 --- /dev/null +++ b/qt/i2pd_qt/android/res/values/strings.xml @@ -0,0 +1,10 @@ + + + + Can\'t find Ministro service.\nThe application can\'t start. + This application requires Ministro service. Would you like to install it? + Your application encountered a fatal error and cannot continue. + i2pd started + i2pd stopped + i2pd + diff --git a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl new file mode 100644 index 00000000..bbd8116d --- /dev/null +++ b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl @@ -0,0 +1,60 @@ +/* + Copyright (c) 2011-2013, BogDan Vatra + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +package org.kde.necessitas.ministro; + +import org.kde.necessitas.ministro.IMinistroCallback; + +interface IMinistro +{ +/** +* Check/download required libs to run the application +* +* param callback - interface used by Minsitro service to notify the client when the loader is ready +* param parameters +* parameters fields: +* * Key Name Key type Explanations +* "sources" StringArray Sources list from where Ministro will download the libs. Make sure you are using ONLY secure locations. +* "repository" String Overwrites the default Ministro repository. Possible values: default, stable, testing and unstable +* "required.modules" StringArray Required modules by your application +* "application.title" String Application name, used to show more informations to user +* "qt.provider" String Qt libs provider, currently only "necessitas" is supported. +* "minimum.ministro.api" Integer Minimum Ministro API level, used to check if Ministro service compatible with your application. Current API Level is 3 ! +* "minimum.qt.version" Integer Minimim Qt version (e.g. 0x040800, which means Qt 4.8.0, check http://qt-project.org/doc/qt-4.8/qtglobal.html#QT_VERSION)! +*/ + void requestLoader(in IMinistroCallback callback, in Bundle parameters); +} diff --git a/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl new file mode 100644 index 00000000..f19caa69 --- /dev/null +++ b/qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl @@ -0,0 +1,65 @@ +/* + Copyright (c) 2011-2013, BogDan Vatra + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.kde.necessitas.ministro; + +oneway interface IMinistroCallback { +/** +* This method is called by the Ministro service back into the application which +* implements this interface. +* +* param in - loaderParams +* loaderParams fields: +* * Key Name Key type Explanations +* * "error.code" Integer See below +* * "error.message" String Missing if no error, otherwise will contain the error message translated into phone language where available. +* * "dex.path" String The list of jar/apk files containing classes and resources, needed to be passed to application DexClassLoader +* * "lib.path" String The list of directories containing native libraries; may be missing, needed to be passed to application DexClassLoader +* * "loader.class.name" String Loader class name. +* +* "error.code" field possible errors: +* - 0 no error. +* - 1 incompatible Ministro version. Ministro needs to be upgraded. +* - 2 not all modules could be satisfy. +* - 3 invalid parameters +* - 4 invalid qt version +* - 5 download canceled +* +* The parameter contains additional fields which are used by the loader to start your application, so it must be passed to the loader. +*/ + + void loaderReady(in Bundle loaderParams); +} diff --git a/qt/i2pd_qt/android/src/org/purplei2p/i2pd/I2PDMainActivity.java b/qt/i2pd_qt/android/src/org/purplei2p/i2pd/I2PDMainActivity.java new file mode 100644 index 00000000..23b32312 --- /dev/null +++ b/qt/i2pd_qt/android/src/org/purplei2p/i2pd/I2PDMainActivity.java @@ -0,0 +1,97 @@ +package org.purplei2p.i2pd; + +import org.qtproject.qt5.android.bindings.QtActivity; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; + +public class I2PDMainActivity extends QtActivity +{ + + private static I2PDMainActivity instance; + + public I2PDMainActivity() {} + + /* (non-Javadoc) + * @see org.qtproject.qt5.android.bindings.QtActivity#onCreate(android.os.Bundle) + */ + @Override + public void onCreate(Bundle savedInstanceState) { + I2PDMainActivity.setInstance(this); + super.onCreate(savedInstanceState); + + //set the app be foreground (do not unload when RAM needed) + doBindService(); + } + + /* (non-Javadoc) + * @see org.qtproject.qt5.android.bindings.QtActivity#onDestroy() + */ + @Override + protected void onDestroy() { + I2PDMainActivity.setInstance(null); + doUnbindService(); + super.onDestroy(); + } + + public static I2PDMainActivity getInstance() { + return instance; + } + + private static void setInstance(I2PDMainActivity instance) { + I2PDMainActivity.instance = instance; + } + + + +// private LocalService mBoundService; + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the service object we can use to + // interact with the service. Because we have bound to a explicit + // service that we know is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. +// mBoundService = ((LocalService.LocalBinder)service).getService(); + + // Tell the user about this for our demo. +// Toast.makeText(Binding.this, R.string.local_service_connected, +// Toast.LENGTH_SHORT).show(); + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + // Because it is running in our same process, we should never + // see this happen. +// mBoundService = null; +// Toast.makeText(Binding.this, R.string.local_service_disconnected, +// Toast.LENGTH_SHORT).show(); + } + }; + + private boolean mIsBound; + + private void doBindService() { + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + bindService(new Intent(this, + LocalService.class), mConnection, Context.BIND_AUTO_CREATE); + mIsBound = true; + } + + void doUnbindService() { + if (mIsBound) { + // Detach our existing connection. + unbindService(mConnection); + mIsBound = false; + } + } +} diff --git a/qt/i2pd_qt/android/src/org/purplei2p/i2pd/LocalService.java b/qt/i2pd_qt/android/src/org/purplei2p/i2pd/LocalService.java new file mode 100644 index 00000000..4c158c08 --- /dev/null +++ b/qt/i2pd_qt/android/src/org/purplei2p/i2pd/LocalService.java @@ -0,0 +1,92 @@ +package org.purplei2p.i2pd; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.util.Log; +import android.widget.Toast; + +public class LocalService extends Service { +// private NotificationManager mNM; + + // Unique Identification Number for the Notification. + // We use it on Notification start, and to cancel it. + private int NOTIFICATION = R.string.local_service_started; + + /** + * Class for clients to access. Because we know this service always + * runs in the same process as its clients, we don't need to deal with + * IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + return LocalService.this; + } + } + + @Override + public void onCreate() { +// mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + + // Display a notification about us starting. We put an icon in the status bar. + showNotification(); + + + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i("LocalService", "Received start id " + startId + ": " + intent); + return START_NOT_STICKY; + } + + @Override + public void onDestroy() { + // Cancel the persistent notification. + //mNM.cancel(NOTIFICATION); + stopForeground(true); + + // Tell the user we stopped. + Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show(); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + // This is the object that receives interactions from clients. See + // RemoteService for a more complete example. + private final IBinder mBinder = new LocalBinder(); + + /** + * Show a notification while this service is running. + */ + private void showNotification() { + // In this sample, we'll use the same text for the ticker and the expanded notification + CharSequence text = getText(R.string.local_service_started); + + // The PendingIntent to launch our activity if the user selects this notification + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, I2PDMainActivity.class), 0); + + // Set the info for the views that show in the notification panel. + Notification notification = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.itoopie_notification_icon) // the status icon + .setTicker(text) // the status text + .setWhen(System.currentTimeMillis()) // the time stamp + .setContentTitle(getText(R.string.local_service_label)) // the label of the entry + .setContentText(text) // the contents of the entry + .setContentIntent(contentIntent) // The intent to send when the entry is clicked + .build(); + + // Send the notification. + //mNM.notify(NOTIFICATION, notification); + startForeground(NOTIFICATION, notification); + } +} \ No newline at end of file diff --git a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java new file mode 100644 index 00000000..677e8f46 --- /dev/null +++ b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -0,0 +1,1621 @@ +/* + Copyright (c) 2012-2013, BogDan Vatra + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; + +import org.kde.necessitas.ministro.IMinistro; +import org.kde.necessitas.ministro.IMinistroCallback; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageInfo; +import android.content.res.Configuration; +import android.content.res.Resources.Theme; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; +import dalvik.system.DexClassLoader; + +//@ANDROID-11 +import android.app.Fragment; +import android.view.ActionMode; +import android.view.ActionMode.Callback; +//@ANDROID-11 + + +public class QtActivity extends Activity +{ + private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished + private static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file) + private static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin + private static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0 + + private static final String ERROR_CODE_KEY = "error.code"; + private static final String ERROR_MESSAGE_KEY = "error.message"; + private static final String DEX_PATH_KEY = "dex.path"; + private static final String LIB_PATH_KEY = "lib.path"; + private static final String LOADER_CLASS_NAME_KEY = "loader.class.name"; + private static final String NATIVE_LIBRARIES_KEY = "native.libraries"; + private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; + private static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; + private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; + private static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id"; + private static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id"; + private static final String MAIN_LIBRARY_KEY = "main.library"; + private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; + private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level"; + private static final String EXTRACT_STYLE_KEY = "extract.android.style"; + + /// Ministro server parameter keys + private static final String REQUIRED_MODULES_KEY = "required.modules"; + private static final String APPLICATION_TITLE_KEY = "application.title"; + private static final String MINIMUM_MINISTRO_API_KEY = "minimum.ministro.api"; + private static final String MINIMUM_QT_VERSION_KEY = "minimum.qt.version"; + private static final String SOURCES_KEY = "sources"; // needs MINISTRO_API_LEVEL >=3 !!! + // Use this key to specify any 3rd party sources urls + // Ministro will download these repositories into their + // own folders, check http://community.kde.org/Necessitas/Ministro + // for more details. + + private static final String REPOSITORY_KEY = "repository"; // use this key to overwrite the default ministro repsitory + private static final String ANDROID_THEMES_KEY = "android.themes"; // themes that your application uses + + + public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + + public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_STYLE=1\tQT_USE_ANDROID_NATIVE_DIALOGS=1\t"; + // use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_STYLE - 1 to use the android widget style if available. + // * QT_USE_ANDROID_NATIVE_DIALOGS -1 to use the android native dialogs. + + public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. + // The name of the theme must be the same with any theme from + // http://developer.android.com/reference/android/R.style.html + // The most used themes are: + // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme + // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black + // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light + // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo + // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light + // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault + // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light + + public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. + + private static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded. + private static final int BUFFER_SIZE = 1024; + + private ActivityInfo m_activityInfo = null; // activity info object, used to access the libs and the strings + private DexClassLoader m_classLoader = null; // loader object + private String[] m_sources = {"https://download.qt-project.org/ministro/android/qt5/qt-5.2"}; // Make sure you are using ONLY secure locations + private String m_repository = "default"; // Overwrites the default Ministro repository + // Possible values: + // * default - Ministro default repository set with "Ministro configuration tool". + // By default the stable version is used. Only this or stable repositories should + // be used in production. + // * stable - stable repository, only this and default repositories should be used + // in production. + // * testing - testing repository, DO NOT use this repository in production, + // this repository is used to push a new release, and should be used to test your application. + // * unstable - unstable repository, DO NOT use this repository in production, + // this repository is used to push Qt snapshots. + private String[] m_qtLibs = null; // required qt libs + private int m_displayDensity = -1; + + public QtActivity() + { + if (Build.VERSION.SDK_INT <= 10) { + QT_ANDROID_THEMES = new String[] {"Theme_Light"}; + QT_ANDROID_DEFAULT_THEME = "Theme_Light"; + } + else if ((Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT <= 13) || Build.VERSION.SDK_INT >= 21){ + QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; + QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; + } else { + QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; + QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light"; + } + } + + // this function is used to load and start the loader + private void loadApplication(Bundle loaderParams) + { + try { + final int errorCode = loaderParams.getInt(ERROR_CODE_KEY); + if (errorCode != 0) { + if (errorCode == INCOMPATIBLE_MINISTRO_VERSION) { + downloadUpgradeMinistro(loaderParams.getString(ERROR_MESSAGE_KEY)); + return; + } + + // fatal error, show the error and quit + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + errorDialog.setMessage(loaderParams.getString(ERROR_MESSAGE_KEY)); + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + return; + } + + // add all bundled Qt libs to loader params + ArrayList libs = new ArrayList(); + if ( m_activityInfo.metaData.containsKey("android.app.bundled_libs_resource_id") ) + libs.addAll(Arrays.asList(getResources().getStringArray(m_activityInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); + + String libName = null; + if ( m_activityInfo.metaData.containsKey("android.app.lib_name") ) { + libName = m_activityInfo.metaData.getString("android.app.lib_name"); + loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function + } + + loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs); + loaderParams.putInt(NECESSITAS_API_LEVEL_KEY, NECESSITAS_API_LEVEL); + + // load and start QtLoader class + m_classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files + getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written. + loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists) + getClassLoader()); // parent loader + + @SuppressWarnings("rawtypes") + Class loaderClass = m_classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class + Object qtLoader = loaderClass.newInstance(); // create an instance + Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication", + Activity.class, + ClassLoader.class, + Bundle.class); + if (!(Boolean)prepareAppMethod.invoke(qtLoader, this, m_classLoader, loaderParams)) + throw new Exception(""); + + QtApplication.setQtActivityDelegate(qtLoader); + + // now load the application library so it's accessible from this class loader + if (libName != null) + System.loadLibrary(libName); + + Method startAppMethod=qtLoader.getClass().getMethod("startApplication"); + if (!(Boolean)startAppMethod.invoke(qtLoader)) + throw new Exception(""); + + } catch (Exception e) { + e.printStackTrace(); + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + if (m_activityInfo.metaData.containsKey("android.app.fatal_error_msg")) + errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.fatal_error_msg")); + else + errorDialog.setMessage("Fatal error, your application can't be started."); + + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + } + } + + private ServiceConnection m_ministroConnection=new ServiceConnection() { + private IMinistro m_service = null; + @Override + public void onServiceConnected(ComponentName name, IBinder service) + { + m_service = IMinistro.Stub.asInterface(service); + try { + if (m_service != null) { + Bundle parameters = new Bundle(); + parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); + parameters.putString(APPLICATION_TITLE_KEY, (String)QtActivity.this.getTitle()); + parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); + parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); + parameters.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES); + if (APPLICATION_PARAMETERS != null) + parameters.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); + parameters.putStringArray(SOURCES_KEY, m_sources); + parameters.putString(REPOSITORY_KEY, m_repository); + if (QT_ANDROID_THEMES != null) + parameters.putStringArray(ANDROID_THEMES_KEY, QT_ANDROID_THEMES); + m_service.requestLoader(m_ministroCallback, parameters); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub() { + // this function is called back by Ministro. + @Override + public void loaderReady(final Bundle loaderParams) throws RemoteException { + runOnUiThread(new Runnable() { + @Override + public void run() { + unbindService(m_ministroConnection); + loadApplication(loaderParams); + } + }); + } + }; + + @Override + public void onServiceDisconnected(ComponentName name) { + m_service = null; + } + }; + + private void downloadUpgradeMinistro(String msg) + { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(this); + downloadDialog.setMessage(msg); + downloadDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + try { + Uri uri = Uri.parse("market://search?q=pname:org.kde.necessitas.ministro"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + startActivityForResult(intent, MINISTRO_INSTALL_REQUEST_CODE); + } catch (Exception e) { + e.printStackTrace(); + ministroNotFound(); + } + } + }); + + downloadDialog.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + QtActivity.this.finish(); + } + }); + downloadDialog.show(); + } + + private void ministroNotFound() + { + AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); + + if (m_activityInfo.metaData.containsKey("android.app.ministro_not_found_msg")) + errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.ministro_not_found_msg")); + else + errorDialog.setMessage("Can't find Ministro service.\nThe application can't start."); + + errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + } + + static private void copyFile(InputStream inputStream, OutputStream outputStream) + throws IOException + { + byte[] buffer = new byte[BUFFER_SIZE]; + + int count; + while ((count = inputStream.read(buffer)) > 0) + outputStream.write(buffer, 0, count); + } + + + private void copyAsset(String source, String destination) + throws IOException + { + // Already exists, we don't have to do anything + File destinationFile = new File(destination); + if (destinationFile.exists()) + return; + + File parentDirectory = destinationFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + destinationFile.createNewFile(); + + AssetManager assetsManager = getAssets(); + InputStream inputStream = assetsManager.open(source); + OutputStream outputStream = new FileOutputStream(destinationFile); + copyFile(inputStream, outputStream); + + inputStream.close(); + outputStream.close(); + } + + private static void createBundledBinary(String source, String destination) + throws IOException + { + // Already exists, we don't have to do anything + File destinationFile = new File(destination); + if (destinationFile.exists()) + return; + + File parentDirectory = destinationFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + destinationFile.createNewFile(); + + InputStream inputStream = new FileInputStream(source); + OutputStream outputStream = new FileOutputStream(destinationFile); + copyFile(inputStream, outputStream); + + inputStream.close(); + outputStream.close(); + } + + private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + long cacheVersion = 0; + if (versionFile.exists() && versionFile.canRead()) { + try { + DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile)); + cacheVersion = inputStream.readLong(); + inputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (cacheVersion != packageVersion) { + deleteRecursively(new File(pluginsPrefix)); + return true; + } else { + return false; + } + } + + private void extractBundledPluginsAndImports(String pluginsPrefix) + throws IOException + { + ArrayList libs = new ArrayList(); + + String libsDir = getApplicationInfo().nativeLibraryDir + "/"; + + long packageVersion = -1; + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + packageVersion = packageInfo.lastUpdateTime; + } catch (Exception e) { + e.printStackTrace(); + } + + if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) + return; + + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + File parentDirectory = versionFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + versionFile.createNewFile(); + + DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile)); + outputStream.writeLong(packageVersion); + outputStream.close(); + } + + { + String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; + java.util.Set keys = m_activityInfo.metaData.keySet(); + if (m_activityInfo.metaData.containsKey(key)) { + String[] list = getResources().getStringArray(m_activityInfo.metaData.getInt(key)); + + for (String bundledImportBinary : list) { + String[] split = bundledImportBinary.split(":"); + String sourceFileName = libsDir + split[0]; + String destinationFileName = pluginsPrefix + split[1]; + createBundledBinary(sourceFileName, destinationFileName); + } + } + } + + { + String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY; + if (m_activityInfo.metaData.containsKey(key)) { + String[] list = getResources().getStringArray(m_activityInfo.metaData.getInt(key)); + + for (String fileName : list) { + String[] split = fileName.split(":"); + String sourceFileName = split[0]; + String destinationFileName = pluginsPrefix + split[1]; + copyAsset(sourceFileName, destinationFileName); + } + } + + } + } + + private void deleteRecursively(File directory) + { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) + deleteRecursively(file); + else + file.delete(); + } + + directory.delete(); + } + } + + private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) + { + File newCache = new File(localPrefix); + if (!newCache.exists()) { + { + File oldPluginsCache = new File(oldLocalPrefix + "plugins/"); + if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) + deleteRecursively(oldPluginsCache); + } + + { + File oldImportsCache = new File(oldLocalPrefix + "imports/"); + if (oldImportsCache.exists() && oldImportsCache.isDirectory()) + deleteRecursively(oldImportsCache); + } + + { + File oldQmlCache = new File(oldLocalPrefix + "qml/"); + if (oldQmlCache.exists() && oldQmlCache.isDirectory()) + deleteRecursively(oldQmlCache); + } + } + } + + private void startApp(final boolean firstStart) + { + try { + if (m_activityInfo.metaData.containsKey("android.app.qt_sources_resource_id")) { + int resourceId = m_activityInfo.metaData.getInt("android.app.qt_sources_resource_id"); + m_sources = getResources().getStringArray(resourceId); + } + + if (m_activityInfo.metaData.containsKey("android.app.repository")) + m_repository = m_activityInfo.metaData.getString("android.app.repository"); + + if (m_activityInfo.metaData.containsKey("android.app.qt_libs_resource_id")) { + int resourceId = m_activityInfo.metaData.getInt("android.app.qt_libs_resource_id"); + m_qtLibs = getResources().getStringArray(resourceId); + } + + if (m_activityInfo.metaData.containsKey("android.app.use_local_qt_libs") + && m_activityInfo.metaData.getInt("android.app.use_local_qt_libs") == 1) { + ArrayList libraryList = new ArrayList(); + + + String localPrefix = "/data/local/tmp/qt/"; + if (m_activityInfo.metaData.containsKey("android.app.libs_prefix")) + localPrefix = m_activityInfo.metaData.getString("android.app.libs_prefix"); + + String pluginsPrefix = localPrefix; + + boolean bundlingQtLibs = false; + if (m_activityInfo.metaData.containsKey("android.app.bundle_local_qt_libs") + && m_activityInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) { + localPrefix = getApplicationInfo().dataDir + "/"; + pluginsPrefix = localPrefix + "qt-reserved-files/"; + cleanOldCacheIfNecessary(localPrefix, pluginsPrefix); + extractBundledPluginsAndImports(pluginsPrefix); + bundlingQtLibs = true; + } + + if (m_qtLibs != null) { + for (int i=0;i 0) { + if (lib.startsWith("lib/")) + libraryList.add(localPrefix + lib); + else + libraryList.add(pluginsPrefix + lib); + } + } + } + + + String dexPaths = new String(); + String pathSeparator = System.getProperty("path.separator", ":"); + if (!bundlingQtLibs && m_activityInfo.metaData.containsKey("android.app.load_local_jars")) { + String[] jarFiles = m_activityInfo.metaData.getString("android.app.load_local_jars").split(":"); + for (String jar:jarFiles) { + if (jar.length() > 0) { + if (dexPaths.length() > 0) + dexPaths += pathSeparator; + dexPaths += localPrefix + jar; + } + } + } + + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, 0); + loaderParams.putString(DEX_PATH_KEY, dexPaths); + loaderParams.putString(LOADER_CLASS_NAME_KEY, "org.qtproject.qt5.android.QtActivityDelegate"); + if (m_activityInfo.metaData.containsKey("android.app.static_init_classes")) { + loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, + m_activityInfo.metaData.getString("android.app.static_init_classes").split(":")); + } + loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList); + + + String themePath = getApplicationInfo().dataDir + "/qt-reserved-files/android-style/"; + String stylePath = themePath + m_displayDensity + "/"; + if (!(new File(stylePath)).exists()) + loaderParams.putString(EXTRACT_STYLE_KEY, stylePath); + ENVIRONMENT_VARIABLES += "\tMINISTRO_ANDROID_STYLE_PATH=" + stylePath + + "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath; + + loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES + + "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" + + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" + + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"); + + if (APPLICATION_PARAMETERS != null) { + loaderParams.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); + } else { + Intent intent = getIntent(); + if (intent != null) { + String parameters = intent.getStringExtra("applicationArguments"); + if (parameters != null) + loaderParams.putString(APPLICATION_PARAMETERS_KEY, parameters.replace(' ', '\t')); + } + } + + loadApplication(loaderParams); + return; + } + + try { + if (!bindService(new Intent(org.kde.necessitas.ministro.IMinistro.class.getCanonicalName()), + m_ministroConnection, + Context.BIND_AUTO_CREATE)) { + throw new SecurityException(""); + } + } catch (Exception e) { + if (firstStart) { + String msg = "This application requires Ministro service. Would you like to install it?"; + if (m_activityInfo.metaData.containsKey("android.app.ministro_needed_msg")) + msg = m_activityInfo.metaData.getString("android.app.ministro_needed_msg"); + downloadUpgradeMinistro(msg); + } else { + ministroNotFound(); + } + } + } catch (Exception e) { + Log.e(QtApplication.QtTAG, "Can't create main activity", e); + } + } + + + + /////////////////////////// forward all notifications //////////////////////////// + /////////////////////////// Super class calls //////////////////////////////////// + /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyEvent, event); + else + return super.dispatchKeyEvent(event); + } + public boolean super_dispatchKeyEvent(KeyEvent event) + { + return super.dispatchKeyEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchPopulateAccessibilityEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchPopulateAccessibilityEvent, event); + else + return super.dispatchPopulateAccessibilityEvent(event); + } + public boolean super_dispatchPopulateAccessibilityEvent(AccessibilityEvent event) + { + return super_dispatchPopulateAccessibilityEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTouchEvent, ev); + else + return super.dispatchTouchEvent(ev); + } + public boolean super_dispatchTouchEvent(MotionEvent event) + { + return super.dispatchTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean dispatchTrackballEvent(MotionEvent ev) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchTrackballEvent, ev); + else + return super.dispatchTrackballEvent(ev); + } + public boolean super_dispatchTrackballEvent(MotionEvent event) + { + return super.dispatchTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + + if (QtApplication.m_delegateObject != null && QtApplication.onActivityResult != null) { + QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data); + return; + } + if (requestCode == MINISTRO_INSTALL_REQUEST_CODE) + startApp(false); + super.onActivityResult(requestCode, resultCode, data); + } + public void super_onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + } + //--------------------------------------------------------------------------- + + @Override + protected void onApplyThemeResource(Theme theme, int resid, boolean first) + { + if (!QtApplication.invokeDelegate(theme, resid, first).invoked) + super.onApplyThemeResource(theme, resid, first); + } + public void super_onApplyThemeResource(Theme theme, int resid, boolean first) + { + super.onApplyThemeResource(theme, resid, first); + } + //--------------------------------------------------------------------------- + + + @Override + protected void onChildTitleChanged(Activity childActivity, CharSequence title) + { + if (!QtApplication.invokeDelegate(childActivity, title).invoked) + super.onChildTitleChanged(childActivity, title); + } + public void super_onChildTitleChanged(Activity childActivity, CharSequence title) + { + super.onChildTitleChanged(childActivity, title); + } + //--------------------------------------------------------------------------- + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + if (!QtApplication.invokeDelegate(newConfig).invoked) + super.onConfigurationChanged(newConfig); + } + public void super_onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + } + //--------------------------------------------------------------------------- + + @Override + public void onContentChanged() + { + if (!QtApplication.invokeDelegate().invoked) + super.onContentChanged(); + } + public void super_onContentChanged() + { + super.onContentChanged(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onContextItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onContextItemSelected(item); + } + public boolean super_onContextItemSelected(MenuItem item) + { + return super.onContextItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onContextMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onContextMenuClosed(menu); + } + public void super_onContextMenuClosed(Menu menu) + { + super.onContextMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + try { + m_activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); + for (Field f : Class.forName("android.R$style").getDeclaredFields()) { + if (f.getInt(null) == m_activityInfo.getThemeResource()) { + QT_ANDROID_THEMES = new String[] {f.getName()}; + QT_ANDROID_DEFAULT_THEME = f.getName(); + } + } + } catch (Exception e) { + e.printStackTrace(); + finish(); + return; + } + + try { + setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null)); + } catch (Exception e) { + e.printStackTrace(); + } + + if (Build.VERSION.SDK_INT > 10) { + try { + requestWindowFeature(Window.class.getField("FEATURE_ACTION_BAR").getInt(null)); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + + if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) { + QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState); + return; + } + + m_displayDensity = getResources().getDisplayMetrics().densityDpi; + + ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME + + "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t"; + + if (null == getLastNonConfigurationInstance()) { + // if splash screen is defined, then show it + if (m_activityInfo.metaData.containsKey("android.app.splash_screen_drawable")) + getWindow().setBackgroundDrawableResource(m_activityInfo.metaData.getInt("android.app.splash_screen_drawable")); + else + getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); + + if (m_activityInfo.metaData.containsKey("android.app.background_running") + && m_activityInfo.metaData.getBoolean("android.app.background_running")) { + ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t"; + } else { + ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t"; + } + + if (m_activityInfo.metaData.containsKey("android.app.auto_screen_scale_factor") + && m_activityInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) { + ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t"; + } + + startApp(true); + } + } + //--------------------------------------------------------------------------- + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + if (!QtApplication.invokeDelegate(menu, v, menuInfo).invoked) + super.onCreateContextMenu(menu, v, menuInfo); + } + public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + } + //--------------------------------------------------------------------------- + + @Override + public CharSequence onCreateDescription() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (CharSequence)res.methodReturns; + else + return super.onCreateDescription(); + } + public CharSequence super_onCreateDescription() + { + return super.onCreateDescription(); + } + //--------------------------------------------------------------------------- + + @Override + protected Dialog onCreateDialog(int id) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id); + } + public Dialog super_onCreateDialog(int id) + { + return super.onCreateDialog(id); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateOptionsMenu(menu); + } + public boolean super_onCreateOptionsMenu(Menu menu) + { + return super.onCreateOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreatePanelMenu(featureId, menu); + } + public boolean super_onCreatePanelMenu(int featureId, Menu menu) + { + return super.onCreatePanelMenu(featureId, menu); + } + //--------------------------------------------------------------------------- + + + @Override + public View onCreatePanelView(int featureId) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreatePanelView(featureId); + } + public View super_onCreatePanelView(int featureId) + { + return super.onCreatePanelView(featureId); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(outBitmap, canvas); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onCreateThumbnail(outBitmap, canvas); + } + public boolean super_onCreateThumbnail(Bitmap outBitmap, Canvas canvas) + { + return super.onCreateThumbnail(outBitmap, canvas); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(name, context, attrs); + } + public View super_onCreateView(String name, Context context, AttributeSet attrs) + { + return super.onCreateView(name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + protected void onDestroy() + { + super.onDestroy(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event); + else + return super.onKeyDown(keyCode, event); + } + public boolean super_onKeyDown(int keyCode, KeyEvent event) + { + return super.onKeyDown(keyCode, event); + } + //--------------------------------------------------------------------------- + + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyMultiple != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyMultiple, keyCode, repeatCount, event); + else + return super.onKeyMultiple(keyCode, repeatCount, event); + } + public boolean super_onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) + { + return super.onKeyMultiple(keyCode, repeatCount, event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event); + else + return super.onKeyUp(keyCode, event); + } + public boolean super_onKeyUp(int keyCode, KeyEvent event) + { + return super.onKeyUp(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public void onLowMemory() + { + if (!QtApplication.invokeDelegate().invoked) + super.onLowMemory(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuItemSelected(featureId, item); + } + public boolean super_onMenuItemSelected(int featureId, MenuItem item) + { + return super.onMenuItemSelected(featureId, item); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onMenuOpened(int featureId, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onMenuOpened(featureId, menu); + } + public boolean super_onMenuOpened(int featureId, Menu menu) + { + return super.onMenuOpened(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onNewIntent(Intent intent) + { + if (!QtApplication.invokeDelegate(intent).invoked) + super.onNewIntent(intent); + } + public void super_onNewIntent(Intent intent) + { + super.onNewIntent(intent); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(item); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onOptionsItemSelected(item); + } + public boolean super_onOptionsItemSelected(MenuItem item) + { + return super.onOptionsItemSelected(item); + } + //--------------------------------------------------------------------------- + + @Override + public void onOptionsMenuClosed(Menu menu) + { + if (!QtApplication.invokeDelegate(menu).invoked) + super.onOptionsMenuClosed(menu); + } + public void super_onOptionsMenuClosed(Menu menu) + { + super.onOptionsMenuClosed(menu); + } + //--------------------------------------------------------------------------- + + @Override + public void onPanelClosed(int featureId, Menu menu) + { + if (!QtApplication.invokeDelegate(featureId, menu).invoked) + super.onPanelClosed(featureId, menu); + } + public void super_onPanelClosed(int featureId, Menu menu) + { + super.onPanelClosed(featureId, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPause() + { + super.onPause(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostCreate(Bundle savedInstanceState) + { + super.onPostCreate(savedInstanceState); + QtApplication.invokeDelegate(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPostResume() + { + super.onPostResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog) + { + if (!QtApplication.invokeDelegate(id, dialog).invoked) + super.onPrepareDialog(id, dialog); + } + public void super_onPrepareDialog(int id, Dialog dialog) + { + super.onPrepareDialog(id, dialog); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPrepareOptionsMenu(menu); + } + public boolean super_onPrepareOptionsMenu(Menu menu) + { + return super.onPrepareOptionsMenu(menu); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(featureId, view, menu); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onPreparePanel(featureId, view, menu); + } + public boolean super_onPreparePanel(int featureId, View view, Menu menu) + { + return super.onPreparePanel(featureId, view, menu); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestart() + { + super.onRestart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) + { + if (!QtApplication.invokeDelegate(savedInstanceState).invoked) + super.onRestoreInstanceState(savedInstanceState); + } + public void super_onRestoreInstanceState(Bundle savedInstanceState) + { + super.onRestoreInstanceState(savedInstanceState); + } + //--------------------------------------------------------------------------- + + @Override + protected void onResume() + { + super.onResume(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + public Object onRetainNonConfigurationInstance() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return res.methodReturns; + else + return super.onRetainNonConfigurationInstance(); + } + public Object super_onRetainNonConfigurationInstance() + { + return super.onRetainNonConfigurationInstance(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onSaveInstanceState(Bundle outState) + { + if (!QtApplication.invokeDelegate(outState).invoked) + super.onSaveInstanceState(outState); + } + public void super_onSaveInstanceState(Bundle outState) + { + super.onSaveInstanceState(outState); + + } + //--------------------------------------------------------------------------- + + @Override + public boolean onSearchRequested() + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(); + if (res.invoked) + return (Boolean)res.methodReturns; + else + return super.onSearchRequested(); + } + public boolean super_onSearchRequested() + { + return super.onSearchRequested(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStart() + { + super.onStart(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onStop() + { + super.onStop(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onTitleChanged(CharSequence title, int color) + { + if (!QtApplication.invokeDelegate(title, color).invoked) + super.onTitleChanged(title, color); + } + public void super_onTitleChanged(CharSequence title, int color) + { + super.onTitleChanged(title, color); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTouchEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTouchEvent, event); + else + return super.onTouchEvent(event); + } + public boolean super_onTouchEvent(MotionEvent event) + { + return super.onTouchEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onTrackballEvent(MotionEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onTrackballEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onTrackballEvent, event); + else + return super.onTrackballEvent(event); + } + public boolean super_onTrackballEvent(MotionEvent event) + { + return super.onTrackballEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onUserInteraction() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserInteraction(); + } + public void super_onUserInteraction() + { + super.onUserInteraction(); + } + //--------------------------------------------------------------------------- + + @Override + protected void onUserLeaveHint() + { + if (!QtApplication.invokeDelegate().invoked) + super.onUserLeaveHint(); + } + public void super_onUserLeaveHint() + { + super.onUserLeaveHint(); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowAttributesChanged(LayoutParams params) + { + if (!QtApplication.invokeDelegate(params).invoked) + super.onWindowAttributesChanged(params); + } + public void super_onWindowAttributesChanged(LayoutParams params) + { + super.onWindowAttributesChanged(params); + } + //--------------------------------------------------------------------------- + + @Override + public void onWindowFocusChanged(boolean hasFocus) + { + if (!QtApplication.invokeDelegate(hasFocus).invoked) + super.onWindowFocusChanged(hasFocus); + } + public void super_onWindowFocusChanged(boolean hasFocus) + { + super.onWindowFocusChanged(hasFocus); + } + //--------------------------------------------------------------------------- + + //////////////// Activity API 5 ///////////// +//@ANDROID-5 + @Override + public void onAttachedToWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onAttachedToWindow(); + } + public void super_onAttachedToWindow() + { + super.onAttachedToWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public void onBackPressed() + { + if (!QtApplication.invokeDelegate().invoked) + super.onBackPressed(); + } + public void super_onBackPressed() + { + super.onBackPressed(); + } + //--------------------------------------------------------------------------- + + @Override + public void onDetachedFromWindow() + { + if (!QtApplication.invokeDelegate().invoked) + super.onDetachedFromWindow(); + } + public void super_onDetachedFromWindow() + { + super.onDetachedFromWindow(); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyLongPress != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyLongPress, keyCode, event); + else + return super.onKeyLongPress(keyCode, event); + } + public boolean super_onKeyLongPress(int keyCode, KeyEvent event) + { + return super.onKeyLongPress(keyCode, event); + } + //--------------------------------------------------------------------------- +//@ANDROID-5 + +//////////////// Activity API 8 ///////////// +//@ANDROID-8 +@Override + protected Dialog onCreateDialog(int id, Bundle args) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(id, args); + if (res.invoked) + return (Dialog)res.methodReturns; + else + return super.onCreateDialog(id, args); + } + public Dialog super_onCreateDialog(int id, Bundle args) + { + return super.onCreateDialog(id, args); + } + //--------------------------------------------------------------------------- + + @Override + protected void onPrepareDialog(int id, Dialog dialog, Bundle args) + { + if (!QtApplication.invokeDelegate(id, dialog, args).invoked) + super.onPrepareDialog(id, dialog, args); + } + public void super_onPrepareDialog(int id, Dialog dialog, Bundle args) + { + super.onPrepareDialog(id, dialog, args); + } + //--------------------------------------------------------------------------- +//@ANDROID-8 + //////////////// Activity API 11 ///////////// + +//@ANDROID-11 + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.dispatchKeyShortcutEvent != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchKeyShortcutEvent, event); + else + return super.dispatchKeyShortcutEvent(event); + } + public boolean super_dispatchKeyShortcutEvent(KeyEvent event) + { + return super.dispatchKeyShortcutEvent(event); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeFinished(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeFinished(mode); + } + public void super_onActionModeFinished(ActionMode mode) + { + super.onActionModeFinished(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onActionModeStarted(ActionMode mode) + { + if (!QtApplication.invokeDelegate(mode).invoked) + super.onActionModeStarted(mode); + } + public void super_onActionModeStarted(ActionMode mode) + { + super.onActionModeStarted(mode); + } + //--------------------------------------------------------------------------- + + @Override + public void onAttachFragment(Fragment fragment) + { + if (!QtApplication.invokeDelegate(fragment).invoked) + super.onAttachFragment(fragment); + } + public void super_onAttachFragment(Fragment fragment) + { + super.onAttachFragment(fragment); + } + //--------------------------------------------------------------------------- + + @Override + public View onCreateView(View parent, String name, Context context, AttributeSet attrs) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(parent, name, context, attrs); + if (res.invoked) + return (View)res.methodReturns; + else + return super.onCreateView(parent, name, context, attrs); + } + public View super_onCreateView(View parent, String name, Context context, + AttributeSet attrs) { + return super.onCreateView(parent, name, context, attrs); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) + { + if (QtApplication.m_delegateObject != null && QtApplication.onKeyShortcut != null) + return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyShortcut, keyCode,event); + else + return super.onKeyShortcut(keyCode, event); + } + public boolean super_onKeyShortcut(int keyCode, KeyEvent event) + { + return super.onKeyShortcut(keyCode, event); + } + //--------------------------------------------------------------------------- + + @Override + public ActionMode onWindowStartingActionMode(Callback callback) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(callback); + if (res.invoked) + return (ActionMode)res.methodReturns; + else + return super.onWindowStartingActionMode(callback); + } + public ActionMode super_onWindowStartingActionMode(Callback callback) + { + return super.onWindowStartingActionMode(callback); + } + //--------------------------------------------------------------------------- +//@ANDROID-11 + //////////////// Activity API 12 ///////////// + +////@ANDROID-12 +// @Override +// public boolean dispatchGenericMotionEvent(MotionEvent ev) +// { +// if (QtApplication.m_delegateObject != null && QtApplication.dispatchGenericMotionEvent != null) +// return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.dispatchGenericMotionEvent, ev); +// else +// return super.dispatchGenericMotionEvent(ev); +// } +// public boolean super_dispatchGenericMotionEvent(MotionEvent event) +// { +// return super.dispatchGenericMotionEvent(event); +// } +// //--------------------------------------------------------------------------- +// +// @Override +// public boolean onGenericMotionEvent(MotionEvent event) +// { +// if (QtApplication.m_delegateObject != null && QtApplication.onGenericMotionEvent != null) +// return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onGenericMotionEvent, event); +// else +// return super.onGenericMotionEvent(event); +// } +// public boolean super_onGenericMotionEvent(MotionEvent event) +// { +// return super.onGenericMotionEvent(event); +// } +// //--------------------------------------------------------------------------- +////@ANDROID-12 + +} diff --git a/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtApplication.java b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtApplication.java new file mode 100644 index 00000000..c78aeb7f --- /dev/null +++ b/qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtApplication.java @@ -0,0 +1,159 @@ +/* + Copyright (c) 2012-2013, BogDan Vatra + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import android.app.Application; + +public class QtApplication extends Application +{ + public final static String QtTAG = "Qt"; + public static Object m_delegateObject = null; + public static HashMap> m_delegateMethods= new HashMap>(); + public static Method dispatchKeyEvent = null; + public static Method dispatchPopulateAccessibilityEvent = null; + public static Method dispatchTouchEvent = null; + public static Method dispatchTrackballEvent = null; + public static Method onKeyDown = null; + public static Method onKeyMultiple = null; + public static Method onKeyUp = null; + public static Method onTouchEvent = null; + public static Method onTrackballEvent = null; + public static Method onActivityResult = null; + public static Method onCreate = null; + public static Method onKeyLongPress = null; + public static Method dispatchKeyShortcutEvent = null; + public static Method onKeyShortcut = null; + public static Method dispatchGenericMotionEvent = null; + public static Method onGenericMotionEvent = null; + + public static void setQtActivityDelegate(Object listener) + { + QtApplication.m_delegateObject = listener; + + ArrayList delegateMethods = new ArrayList(); + for (Method m : listener.getClass().getMethods()) { + if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt5.android")) + delegateMethods.add(m); + } + + ArrayList applicationFields = new ArrayList(); + for (Field f : QtApplication.class.getFields()) { + if (f.getDeclaringClass().getName().equals(QtApplication.class.getName())) + applicationFields.add(f); + } + + for (Method delegateMethod : delegateMethods) { + try { + QtActivity.class.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes()); + if (QtApplication.m_delegateMethods.containsKey(delegateMethod.getName())) { + QtApplication.m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod); + } else { + ArrayList delegateSet = new ArrayList(); + delegateSet.add(delegateMethod); + QtApplication.m_delegateMethods.put(delegateMethod.getName(), delegateSet); + } + for (Field applicationField:applicationFields) { + if (applicationField.getName().equals(delegateMethod.getName())) { + try { + applicationField.set(null, delegateMethod); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } catch (Exception e) { + } + } + } + + @Override + public void onTerminate() { + if (m_delegateObject != null && m_delegateMethods.containsKey("onTerminate")) + invokeDelegateMethod(m_delegateMethods.get("onTerminate").get(0)); + super.onTerminate(); + } + + public static class InvokeResult + { + public boolean invoked = false; + public Object methodReturns = null; + } + + private static int stackDeep=-1; + public static InvokeResult invokeDelegate(Object... args) + { + InvokeResult result = new InvokeResult(); + if (m_delegateObject == null) + return result; + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (-1 == stackDeep) { + String activityClassName = QtActivity.class.getCanonicalName(); + for (int it=0;it + + + +

    + OpenSSL под Android в Qt + +

    Запись от Wyn размещена 18.01.2016 в 18:22
    Метки android, openssl, qt

    Мини-руководство по тому, как быстро скомпилировать OpenSSL для Android и связать его с проектом Qt.
    +Для Linux.

    +Вначале действия полностью идентичны "расово-верному" руководству по компилянию OpenSSL для Android:
    +Качаем исходники openssl нужной версии с их сайта, качаем setenv-android.sh(все ссылки на закачку выше по ссылке).
    +Ложим их в одну папку. Запускаем консоль, переходим в ней в эту самую папку.
    +Далее:
    BashВыделить код
    1
    +2
    +3
    +
    $ rm -rf openssl-1.0.1g/   # удаляем исходники(вместо версии 1.0.1g - подставляем свою), если они уже были распакованы
    +$ tar xzf openssl-1.0.1g.tar.gz    # распаковываем исходники в подпапку
    +$ chmod a+x setenv-android.sh    # разрешаем setenv-android.sh исполняться
    Редактируем setenv-android.sh, настраивая там _ANDROID_EABI, _ANDROID_ARCH, _ANDROID_API на нужные значения.
    +Дальше возвращаемся в консоль:
    BashВыделить код
    1
    +2
    +3
    +4
    +5
    +6
    +7
    +8
    +9
    +10
    +11
    +
    $ export ANDROID_NDK_ROOT=путь_до_ANDROID_NDK # указываем путь до Android NDK для setenv-android.sh
    +$ . ./setenv-android.sh # запускаем скрипт, чтобы он нам в окружение проставил необходимые далее переменные
    +$ cd openssl-1.0.1g/
    +$ perl -pi -e 's/install: all install_docs install_sw/install: install_docs install_sw/g' Makefile.org
    +# конфигурируем
    +$ ./config shared no-ssl2 no-ssl3 no-comp no-hw no-engine --openssldir=/usr/local/ssl/$ANDROID_API
    +# собираем
    +$ make depend
    +$ make all
    +# устанавливаем
    +$ sudo -E make install CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib
    И тут начинается интересное. Андроид не принимает versioned shared object (это *.so.x и подобные). Казалось бы 2016 год, космические корабли уже давно бороздят просторы Большого театра, но вот те на.

    +Однако, есть обходной приём - нужно заменить *.so.x.x.x на *_x_x_x.so. Простым переименованием файлов данную проблему здесь, разумеется, не решить. Нужно лезть внутрь и переименовывать soname и внутренние ссылки на другие versioned shared object. В интернете есть много способов по подобному переименованию. Большинство из них обещают райскую жизнь с rpl, забывая упомянуть, что утилита уже давно отпета и закопана на большинстве дистрибутивов. Или хитро-хитро редактируют makefile, что в итоге на место левой руки собирается правая нога. В целом множество путей из разряда "как потратить много времени на полную фигню".

    +В итоге предлагаю решить данную проблему методом топора:
    +Качаем hex-редактор, если ещё нет(в моём случае таковым оказался Okteta). Запускаем его из под рута(kdesu okteta), открываем в нём файлы openssldir/lib/libcrypto.so.1.0.0. Заменяем(ctrl+r) в нём символы ".so.1.0.0" на char "_1_0_0.so". Проделываем тоже самое с libssl.so.1.0.0. Всё, теперь осталось только переименовать сами файлы(в libcrypto_1_0_0.so и libssl_1_0_0.so) и поправить ссылки libssl.so и libcrypto.so, чтобы они вели на них.

    +Чтобы подключить и использовать данную библиотеку в проекте нужно добавить в .pro:
    BashВыделить код
    1
    +2
    +3
    +4
    +5
    +
    android: {
    +    INCLUDEPATH += /usr/local/ssl/android-21/include
    +    LIBS += -L/usr/local/ssl/android-21/lib
    +}
    +LIBS += -lcrypto
    А затем в настройках проекта, в Buld/Build Steps/Bulild Android Apk добавить libcrypto_1_0_0.so и libssl_1_0_0.so в список Additional Libraries.

    +На этом всё. + +
    +

    Original: http://www.cyberforum.ru/blogs/748276/blog4086.html

    + + diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro new file mode 100644 index 00000000..c3b38a8c --- /dev/null +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -0,0 +1,192 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-06-14T04:53:04 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = i2pd_qt +TEMPLATE = app +QMAKE_CXXFLAGS *= -std=c++11 + +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/android-ifaddrs.git +# change to your own +BOOST_PATH = /mnt/media/android/Boost-for-Android-Prebuilt +OPENSSL_PATH = /mnt/media/android/OpenSSL-for-Android-Prebuilt +IFADDRS_PATH = /mnt/media/android/android-ifaddrs + +# Steps in Android SDK manager: +# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html +# 2) Check API 11 +# Finally, click Install. + +SOURCES += DaemonQT.cpp\ + mainwindow.cpp \ + ../../HTTPServer.cpp ../../I2PControl.cpp ../../UPnP.cpp ../../Daemon.cpp ../../Config.cpp \ + ../../AddressBook.cpp \ + ../../api.cpp \ + ../../Base.cpp \ + ../../BOB.cpp \ + ../../ClientContext.cpp \ + ../../Crypto.cpp \ + ../../Datagram.cpp \ + ../../Destination.cpp \ + ../../Family.cpp \ + ../../FS.cpp \ + ../../Garlic.cpp \ + ../../HTTP.cpp \ + ../../HTTPProxy.cpp \ + ../../I2CP.cpp \ + ../../I2NPProtocol.cpp \ + ../../I2PEndian.cpp \ + ../../I2PService.cpp \ + ../../I2PTunnel.cpp \ + ../../Identity.cpp \ + ../../LeaseSet.cpp \ + ../../Log.cpp \ + ../../NetDb.cpp \ + ../../NetDbRequests.cpp \ + ../../NTCPSession.cpp \ + ../../Profiling.cpp \ + ../../Reseed.cpp \ + ../../RouterContext.cpp \ + ../../RouterInfo.cpp \ + ../../SAM.cpp \ + ../../Signature.cpp \ + ../../SOCKS.cpp \ + ../../SSU.cpp \ + ../../SSUData.cpp \ + ../../SSUSession.cpp \ + ../../Streaming.cpp \ + ../../TransitTunnel.cpp \ + ../../Transports.cpp \ + ../../Tunnel.cpp \ + ../../TunnelEndpoint.cpp \ + ../../TunnelGateway.cpp \ + ../../TunnelPool.cpp \ + ../../util.cpp \ + ../../i2pd.cpp \ + $$IFADDRS_PATH/ifaddrs.c + +HEADERS += DaemonQT.h mainwindow.h \ + ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \ + ../../AddressBook.h \ + ../../api.h \ + ../../Base.h \ + ../../BOB.h \ + ../../ClientContext.h \ + ../../Crypto.h \ + ../../Datagram.h \ + ../../Destination.h \ + ../../Family.h \ + ../../FS.h \ + ../../Garlic.h \ + ../../HTTP.h \ + ../../HTTPProxy.h \ + ../../I2CP.h \ + ../../I2NPProtocol.h \ + ../../I2PEndian.h \ + ../../I2PService.h \ + ../../I2PTunnel.h \ + ../../Identity.h \ + ../../LeaseSet.h \ + ../../LittleBigEndian.h \ + ../../Log.h \ + ../../NetDb.h \ + ../../NetDbRequests.h \ + ../../NTCPSession.h \ + ../../Profiling.h \ + ../../Queue.h \ + ../../Reseed.h \ + ../../RouterContext.h \ + ../../RouterInfo.h \ + ../../SAM.h \ + ../../Signature.h \ + ../../SOCKS.h \ + ../../SSU.h \ + ../../SSUData.h \ + ../../SSUSession.h \ + ../../Streaming.h \ + ../../Timestamp.h \ + ../../TransitTunnel.h \ + ../../Transports.h \ + ../../TransportSession.h \ + ../../Tunnel.h \ + ../../TunnelBase.h \ + ../../TunnelConfig.h \ + ../../TunnelEndpoint.h \ + ../../TunnelGateway.h \ + ../../TunnelPool.h \ + ../../util.h \ + ../../version.h \ + $$IFADDRS_PATH/ifaddrs.h + +FORMS += mainwindow.ui + +CONFIG += mobility + +MOBILITY = + +LIBS += -lz + +android { +message("Using Android settings") +DEFINES += ANDROID=1 +DEFINES += __ANDROID__ +INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ + $$OPENSSL_PATH/openssl-1.0.2/include \ + $$IFADDRS_PATH +DISTFILES += \ + android/AndroidManifest.xml + +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + +equals(ANDROID_TARGET_ARCH, armeabi-v7a){ + +DEFINES += ANDROID_ARM7A + +# http://stackoverflow.com/a/30235934/529442 +LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ +-lboost_system-gcc-mt-1_53 \ +-lboost_date_time-gcc-mt-1_53 \ +-lboost_filesystem-gcc-mt-1_53 \ +-lboost_program_options-gcc-mt-1_53 \ +-L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl + +PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a + +DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + +ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so +} +equals(ANDROID_TARGET_ARCH, x86){ +# http://stackoverflow.com/a/30235934/529442 +LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ +-lboost_system-gcc-mt-1_53 \ +-lboost_date_time-gcc-mt-1_53 \ +-lboost_filesystem-gcc-mt-1_53 \ +-lboost_program_options-gcc-mt-1_53 \ +-L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl + +PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a + +DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + +ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so +} +} + +linux:!android { +message("Using Linux settings") +LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread +} + diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp new file mode 100644 index 00000000..c1654295 --- /dev/null +++ b/qt/i2pd_qt/mainwindow.cpp @@ -0,0 +1,55 @@ +#include "mainwindow.h" +//#include "ui_mainwindow.h" +#include + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent)/*, + ui(new Ui::MainWindow)*/ +{ + //ui->setupUi(this); + if (objectName().isEmpty()) + setObjectName(QStringLiteral("MainWindow")); + resize(800, 480); + centralWidget = new QWidget(this); + centralWidget->setObjectName(QStringLiteral("centralWidget")); + verticalLayoutWidget = new QWidget(centralWidget); + verticalLayoutWidget->setObjectName(QStringLiteral("verticalLayoutWidget")); + //verticalLayoutWidget->setGeometry(QRect(10, 20, 771, 441)); + verticalLayout1 = new QVBoxLayout(verticalLayoutWidget); + verticalLayout1->setSpacing(6); + verticalLayout1->setContentsMargins(11, 11, 11, 11); + verticalLayout1->setObjectName(QStringLiteral("verticalLayout1")); + verticalLayout1->setContentsMargins(0, 0, 0, 0); + quitButton = new QPushButton(verticalLayoutWidget); + quitButton->setObjectName(QStringLiteral("quitButton")); + QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(quitButton->sizePolicy().hasHeightForWidth()); + quitButton->setSizePolicy(sizePolicy); + + verticalLayout1->addWidget(quitButton); + + setCentralWidget(centralWidget); + + setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0)); + quitButton->setText(QApplication::translate("MainWindow", "Quit", 0)); + + QObject::connect(quitButton, SIGNAL(released()), this, SLOT(handleQuitButton())); + + //QMetaObject::connectSlotsByName(this); +} + +void MainWindow::handleQuitButton() { + qDebug("Quit pressed. Hiding the main window"); + close(); + QApplication::instance()->quit(); +} + +MainWindow::~MainWindow() +{ + qDebug("Destroying main window"); + //QMessageBox::information(0, "Debug", "mw destructor 1"); + //delete ui; + //QMessageBox::information(0, "Debug", "mw destructor 2"); +} diff --git a/qt/i2pd_qt/mainwindow.h b/qt/i2pd_qt/mainwindow.h new file mode 100644 index 00000000..3a172c25 --- /dev/null +++ b/qt/i2pd_qt/mainwindow.h @@ -0,0 +1,38 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void handleQuitButton(); + +private: + QWidget *centralWidget; + QWidget *verticalLayoutWidget; + QVBoxLayout *verticalLayout1; + QPushButton *quitButton; +}; + +#endif // MAINWINDOW_H diff --git a/qt/i2pd_qt/mainwindow.ui b/qt/i2pd_qt/mainwindow.ui new file mode 100644 index 00000000..bdb57867 --- /dev/null +++ b/qt/i2pd_qt/mainwindow.ui @@ -0,0 +1,67 @@ + + + MainWindow + + + + 0 + 0 + 800 + 480 + + + + MainWindow + + + + + + 10 + 20 + 771 + 441 + + + + + + + + 0 + 0 + + + + Quit + + + + + + + + + + + + quitButton + released() + MainWindow + handleQuitButton() + + + 384 + 244 + + + 373 + 419 + + + + + + handleQuitButton() + + diff --git a/util.h b/util.h index f5dbc9aa..9f797158 100644 --- a/util.h +++ b/util.h @@ -7,6 +7,17 @@ #include #include +#ifdef ANDROID +namespace std +{ +template +std::string to_string(T value) +{ + return boost::lexical_cast(value); +} +} +#endif + namespace i2p { namespace util diff --git a/version.h b/version.h index 5d07c596..46d170db 100644 --- a/version.h +++ b/version.h @@ -7,7 +7,7 @@ #define MAKE_VERSION(a,b,c) STRINGIZE(a) "." STRINGIZE(b) "." STRINGIZE(c) #define I2PD_VERSION_MAJOR 2 -#define I2PD_VERSION_MINOR 7 +#define I2PD_VERSION_MINOR 8 #define I2PD_VERSION_MICRO 0 #define I2PD_VERSION_PATCH 0 #define I2PD_VERSION MAKE_VERSION(I2PD_VERSION_MAJOR, I2PD_VERSION_MINOR, I2PD_VERSION_MICRO)