1
0
mirror of https://github.com/nerzhul/ownCloud-SMS-App.git synced 2025-06-05 23:16:27 +00:00

Replace C++ with Cmake with GoLang gomobile

* Add ncsmsgo.arr which is the GoLang gomobile part
See: https://gitlab.com/nerzhul/ncsmsgo
* Android-NDK is not needed anymore
* Android app getVersion() now uses the Golang part
* Use the next getLastHTTPStatus() call in getVersion()
* Android app pushSms() now uses the Golang part
* JNI SmsBuffer has been replaced with GoLang aar bindings
-> This permits to remove some java code & increase performance
* Prepare a insecure switch flag to the Golang client
This commit is contained in:
Loic Blot 2018-02-08 23:19:25 +01:00 committed by Loïc Blot
parent 5317dc4fd9
commit d57810b5d8
30 changed files with 138 additions and 725 deletions

View File

@ -11,6 +11,9 @@ cache:
jdk:
- oraclejdk8
before_install:
- yes | sdkmanager "platforms;android-27"
android:
components:
- tools
@ -23,10 +26,3 @@ android:
- 'google-gdk-license-.+'
- 'android-sdk-preview-license-.+'
- 'android-.*'
before_install:
- wget https://dl.google.com/android/repository/android-ndk-r16-linux-x86_64.zip > /dev/null
- mkdir -p /usr/local/android-sdk/ndk-bundle
- unzip android-ndk-r16-linux-x86_64.zip -d /usr/local/android-sdk/ndk-bundle > /dev/null
- export ANDROID_NDK_HOME="/usr/local/android-sdk/ndk-bundle/android-ndk-r16"
- rm -Rf "${ANDROID_HOME}/cmake"

View File

@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 3.5.0)
set (SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/httpclient.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/json.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/nativesms.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/smsbuffer.cpp
)
find_library(LOGGING_LIBRARY log)
add_library(nativesms SHARED ${SRC_FILES})
target_link_libraries(nativesms ${LOGGING_LIBRARY})

View File

@ -2,6 +2,7 @@ buildscript {
repositories {
mavenCentral()
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
@ -47,11 +48,6 @@ android {
packagingOptions {
exclude 'META-INF/LICENSE.txt'
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
}
repositories {
@ -69,4 +65,5 @@ dependencies {
compile 'in.srain.cube:ultra-ptr:1.0.11'
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
compile 'com.android.support:support-v4:27.0.2'
implementation project(':ncsmsgo')
}

2
ncsmsgo/build.gradle Normal file
View File

@ -0,0 +1,2 @@
configurations.maybeCreate("default")
artifacts.add("default", file('ncsmsgo.aar'))

BIN
ncsmsgo/ncsmsgo.aar Normal file

Binary file not shown.

25
ncsmsgo/ncsmsgo.iml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":ncsmsgo" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":ncsmsgo" />
</configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':ncsmsgo'

View File

@ -1,56 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "httpclient.h"
const char *HTTPClient::classPathName = "fr/unix_experience/owncloud_sms/engine/OCHttpClient";
// See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html
JNINativeMethod HTTPClient::methods[] =
{
DECL_JNIMETHOD(getAllSmsIdsCall, "()Ljava/lang/String;")
DECL_JNIMETHOD(getLastMsgTimestamp, "()Ljava/lang/String;")
DECL_JNIMETHOD(getPushRoute, "()Ljava/lang/String;")
DECL_JNIMETHOD(getVersionCall, "()Ljava/lang/String;")
};
DECL_METHODSIZE(HTTPClient)
// APIv1 calls
#define RCALL_GET_VERSION "/index.php/apps/ocsms/get/apiversion?format=json"
#define RCALL_GET_ALL_SMS_IDS "/index.php/apps/ocsms/get/smsidlist?format=json"
#define RCALL_GET_LAST_MSG_TIMESTAMP "/index.php/apps/ocsms/get/lastmsgtime?format=json"
#define RCALL_PUSH_ROUTE "/index.php/apps/ocsms/push?format=json"
jstring HTTPClient::getVersionCall(JNIEnv *env, jobject)
{
return env->NewStringUTF(RCALL_GET_VERSION);
}
jstring HTTPClient::getAllSmsIdsCall(JNIEnv *env, jobject)
{
return env->NewStringUTF(RCALL_GET_ALL_SMS_IDS);
}
jstring HTTPClient::getLastMsgTimestamp(JNIEnv *env, jobject)
{
return env->NewStringUTF(RCALL_GET_LAST_MSG_TIMESTAMP);
}
jstring HTTPClient::getPushRoute(JNIEnv *env, jobject)
{
return env->NewStringUTF(RCALL_PUSH_ROUTE);
}

View File

@ -1,34 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <jni.h>
#include "macro_helpers.h"
class HTTPClient
{
public:
JNIEXPORT static jstring JNICALL getVersionCall(JNIEnv *env, jobject);
JNIEXPORT static jstring JNICALL getAllSmsIdsCall(JNIEnv *env, jobject);
JNIEXPORT static jstring JNICALL getLastMsgTimestamp(JNIEnv *env, jobject);
JNIEXPORT static jstring JNICALL getPushRoute(JNIEnv *env, jobject);
DECL_JNICLASSATTRS
};

View File

@ -1,73 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "json.h"
#include <iomanip>
#include <sstream>
#include <cstring>
namespace json {
std::string escape_string(const char *str)
{
if (!str) {
return "";
}
std::string result;
// Create a sufficient buffer to escape all chars
result.reserve(strlen(str) * 2 + 3);
result += "\"";
for (const char *c = str; *c != 0; ++c) {
switch (*c) {
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\t':
result += "\\t";
break;
case '\n':
result += "\\n";
break;
case '\f':
result += "\\f";
break;
case '\r':
result += "\\r";
break;
default:
if (is_control_character(*c)) {
std::stringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
<< std::setw(4) << static_cast<int>(*c);
result += oss.str();
} else {
result += *c;
}
break;
}
}
result += "\"";
return result;
}
}

View File

@ -1,29 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
namespace json {
static inline bool is_control_character(char ch)
{ return ch > 0 && ch <= 0x1F; }
std::string escape_string(const char *str);
}

View File

@ -1,29 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define DECL_JNIMETHOD(name, type) \
{#name, type, (void*) &name },
#define DECL_METHODSIZE(T) \
int T::methods_size = sizeof(T::methods) / sizeof(T::methods[0]);
#define DECL_JNICLASSATTRS \
static const char *classPathName; \
static JNINativeMethod methods[]; \
static int methods_size;

View File

@ -1,76 +0,0 @@
/**
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <jni.h>
#include <android/log.h>
#include "httpclient.h"
#include "smsbuffer.h"
#define LOG_TAG "nativesms.cpp"
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Registering class '%s'", className);
if (clazz == NULL) {
__android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "Native registration unable to find class %s", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "RegisterNatives failed for %s", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env,
HTTPClient::classPathName,
HTTPClient::methods,
HTTPClient::methods_size)) {
return JNI_FALSE;
}
if (!registerNativeMethods(env,
SmsBuffer::classPathName,
SmsBuffer::methods,
SmsBuffer::methods_size)) {
return JNI_FALSE;
}
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "NativeSMS library loading...");
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
registerNatives(env);
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "NativeSMS library loaded.");
return JNI_VERSION_1_6;
}

View File

@ -1,162 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <android/log.h>
#include <cassert>
#include <iomanip>
#include "smsbuffer.h"
#include "json.h"
#define LOG_TAG "SmsBuffer"
const char *SmsBuffer::classPathName = "fr/unix_experience/owncloud_sms/jni/SmsBuffer";
// See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html
JNINativeMethod SmsBuffer::methods[] =
{
DECL_JNIMETHOD(createNativeObject, "()J")
DECL_JNIMETHOD(deleteNativeObject, "()V")
DECL_JNIMETHOD(empty, "()Z")
DECL_JNIMETHOD(asRawJsonString, "()Ljava/lang/String;")
DECL_JNIMETHOD(getLastMessageDate, "()J")
DECL_JNIMETHOD(push,
"(IIIJLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V")
DECL_JNIMETHOD(print, "()V")
};
DECL_METHODSIZE(SmsBuffer)
#define SMSBUFFER_CAST \
if (!SmsBuffer::gJava_inited) { \
SmsBuffer::gJava_inited = true; \
SmsBuffer::gJava_mHandle = env->GetFieldID(env->GetObjectClass(self), "mHandle", "J"); \
} \
long ptr = env->GetLongField(self, SmsBuffer::gJava_mHandle); \
SmsBuffer *me = reinterpret_cast<SmsBuffer *>(ptr); \
if (!me) { \
__android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "It's not a SmsBuffer!"); \
assert(false); \
}
bool SmsBuffer::gJava_inited = false;
jfieldID SmsBuffer::gJava_mHandle{};
jlong SmsBuffer::createNativeObject(JNIEnv *env, jobject self)
{
return reinterpret_cast<jlong>(new SmsBuffer());
}
void SmsBuffer::deleteNativeObject(JNIEnv *env, jobject self)
{
SMSBUFFER_CAST
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "deleteNativeObject 0x%li", ptr);
delete reinterpret_cast<SmsBuffer *>(ptr);
}
void SmsBuffer::reset_buffer()
{
m_buffer.clear();
m_buffer_empty = true;
m_sms_count = 0;
m_last_message_date = 0;
}
void SmsBuffer::push(JNIEnv *env, jobject self, jint msg_id, jint mailbox_id, jint type,
jlong date, jstring address, jstring body, jstring read, jstring seen)
{
SMSBUFFER_CAST
me->_push(msg_id, mailbox_id, type, date, env->GetStringUTFChars(address, NULL),
env->GetStringUTFChars(body, NULL), env->GetStringUTFChars(read, NULL),
env->GetStringUTFChars(seen, NULL));
}
void SmsBuffer::_push(int msg_id, int mailbox_id, int type,
long long date, const char *address, const char *body, const char *read,
const char *seen)
{
// If buffer is not empty, we are joining messages
if (!m_buffer_empty) {
m_buffer << ",";
}
// Else, we are starting array
else {
m_buffer << "[";
m_buffer_empty = false;
}
m_buffer << "{\"_id\":" << msg_id << ","
<< "\"mbox\":" << mailbox_id << ","
<< "\"type\":" << type << ","
<< "\"date\":" << date << ","
<< "\"body\":" << json::escape_string(body) << ","
<< "\"address\":" << json::escape_string(address) << ","
<< "\"read\":" << json::escape_string(read) << ","
<< "\"seen\":" << json::escape_string(seen)
<< "}";
if (date > m_last_message_date) {
m_last_message_date = date;
}
m_sms_count++;
}
jboolean SmsBuffer::empty(JNIEnv *env, jobject self)
{
SMSBUFFER_CAST
return (jboolean) (me->_empty() ? 1 : 0);
}
bool SmsBuffer::_empty() const
{
return m_buffer_empty;
}
void SmsBuffer::print(JNIEnv *env, jobject self)
{
SMSBUFFER_CAST
me->_print();
}
void SmsBuffer::_print()
{
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "SmsBuffer content: '%s'",
m_buffer.str().c_str());
}
jstring SmsBuffer::asRawJsonString(JNIEnv *env, jobject self)
{
SMSBUFFER_CAST
std::string result;
me->as_raw_json_string(result);
return env->NewStringUTF(result.c_str());
}
void SmsBuffer::as_raw_json_string(std::string &result)
{
std::stringstream ss;
ss << "{\"smsCount\": " << m_sms_count << ", \"smsDatas\": " << m_buffer.str() << "]}";
result = ss.str();
}
jlong SmsBuffer::getLastMessageDate(JNIEnv *env, jobject self)
{
SMSBUFFER_CAST
return me->_get_last_message_date();
}
long long SmsBuffer::_get_last_message_date() const
{
return m_last_message_date;
}

View File

@ -1,80 +0,0 @@
/**
* Copyright (c) 2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <jni.h>
#include <sstream>
#include "macro_helpers.h"
class SmsBuffer
{
public:
SmsBuffer() = default;
~SmsBuffer() = default;
JNIEXPORT static jlong JNICALL createNativeObject(JNIEnv *env, jobject self);
JNIEXPORT static void JNICALL deleteNativeObject(JNIEnv *env, jobject self);
/*
* push method
*/
JNIEXPORT static void JNICALL push(JNIEnv *env, jobject self, jint msg_id,
jint mailbox_id, jint type, jlong date, jstring address,
jstring body, jstring read, jstring seen);
void _push(int msg_id, int mailbox_id, int type,
long long date, const char *address, const char *body, const char *read,
const char *seen);
/*
* empty method
*/
JNIEXPORT static jboolean JNICALL empty(JNIEnv *env, jobject self);
bool _empty() const;
/*
* print method
*/
JNIEXPORT static void JNICALL print(JNIEnv *env, jobject self);
void _print();
/*
* asRawJsonString method
*/
JNIEXPORT static jstring JNICALL asRawJsonString(JNIEnv *env, jobject self);
void as_raw_json_string(std::string &result);
/*
* getLastMessageDate method
*/
JNIEXPORT static jlong JNICALL getLastMessageDate(JNIEnv *env, jobject self);
long long _get_last_message_date() const;
DECL_JNICLASSATTRS
private:
void reset_buffer();
std::stringstream m_buffer;
uint32_t m_sms_count{0};
bool m_buffer_empty{true};
long long m_last_message_date{0};
static bool gJava_inited;
static jfieldID gJava_mHandle;
};

View File

@ -208,7 +208,7 @@ public class AppCompatListActivity extends AppCompatActivity {
super.onContentChanged();
View emptyView = findViewById(R.id.empty);
mList = (ListView)findViewById(R.id.list);
mList = findViewById(R.id.list);
if (mList == null) {
throw new RuntimeException(
"Your content must have a ListView whose id attribute is " +

View File

@ -44,8 +44,6 @@ import android.widget.TextView;
import com.dd.processbutton.iml.ActionProcessButton;
import org.json.JSONObject;
import java.net.MalformedURLException;
import java.net.URL;
@ -85,11 +83,11 @@ public class LoginActivity extends AppCompatActivity {
actionBar.setDisplayHomeAsUpEnabled(true);
// Set up the login form.
_protocolView = (Spinner) findViewById(R.id.oc_protocol);
_serverView = (EditText) findViewById(R.id.oc_server);
_loginView = (EditText) findViewById(R.id.oc_login);
_protocolView = findViewById(R.id.oc_protocol);
_serverView = findViewById(R.id.oc_server);
_loginView = findViewById(R.id.oc_login);
_passwordView = (EditText) findViewById(R.id.oc_password);
_passwordView = findViewById(R.id.oc_password);
_passwordView
.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
@ -103,7 +101,7 @@ public class LoginActivity extends AppCompatActivity {
}
});
_signInButton = (ActionProcessButton) findViewById(R.id.oc_signin_button);
_signInButton = findViewById(R.id.oc_signin_button);
_signInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
@ -270,8 +268,8 @@ public class LoginActivity extends AppCompatActivity {
_returnCode = 0;
OCHttpClient http = new OCHttpClient(getBaseContext(), _serverURL, _login, _password);
try {
Pair<Integer, JSONObject> response = http.getVersion();
_returnCode = response.first;
Pair<Integer, Integer> vPair = http.getVersion();
_returnCode = vPair.first;
} catch (IllegalArgumentException e) {
Log.w(TAG, "Failed to getVersion, IllegalArgumentException occured: " + e.getMessage());
_returnCode = 597;

View File

@ -76,7 +76,7 @@ public class MainActivity extends AppCompatActivity
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer = findViewById(R.id.drawer_layout);
setupDrawer();
drawer.openDrawer(GravityCompat.START);
}
@ -93,7 +93,7 @@ public class MainActivity extends AppCompatActivity
toggle.syncState();
toggle.setDrawerIndicatorEnabled(true);
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
NavigationView navigationView = findViewById(R.id.nav_view);
assert navigationView != null;
navigationView.setNavigationItemSelectedListener(this);
}

View File

@ -55,12 +55,12 @@ public class ContactListActivity extends AppCompatActivity implements ASyncConta
mObjects = new ArrayList<>();
setContentView(R.layout.restore_activity_contactlist);
mLayout = (SwipeRefreshLayout) findViewById(R.id.contactlist_swipe_container);
mLayout = findViewById(R.id.contactlist_swipe_container);
mAdapter = new ContactListAdapter(getBaseContext(), mObjects);
mContactInfos = (LinearLayout) findViewById(R.id.contactinfos_layout);
ListView contactPhoneListView = (ListView) findViewById(R.id.contact_phonelistView);
mContactInfos = findViewById(R.id.contactinfos_layout);
ListView contactPhoneListView = findViewById(R.id.contact_phonelistView);
mContactPhoneListAdapter = new RecoveryPhoneNumberListViewAdapter(getBaseContext());
assert contactPhoneListView != null;
contactPhoneListView.setAdapter(mContactPhoneListAdapter);
@ -72,7 +72,7 @@ public class ContactListActivity extends AppCompatActivity implements ASyncConta
}
private void createAccountList() {
final ProgressBar contactProgressBar = (ProgressBar) findViewById(R.id.contactlist_pgbar);
final ProgressBar contactProgressBar = findViewById(R.id.contactlist_pgbar);
assert contactProgressBar != null;
String accountName = getIntent().getExtras().getString("account");
@ -112,7 +112,7 @@ public class ContactListActivity extends AppCompatActivity implements ASyncConta
}
private void initSpinner() {
final Spinner sp = (Spinner) findViewById(R.id.contact_spinner);
final Spinner sp = findViewById(R.id.contact_spinner);
assert sp != null;
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override

View File

@ -73,9 +73,9 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
initInterface();
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
final Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
final ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
Button fix_button = findViewById(R.id.button_fix_permissions);
final Button launch_restore = findViewById(R.id.button_launch_restore);
final ProgressBar pb = findViewById(R.id.progressbar_restore);
final RestoreMessagesActivity me = this;
fix_button.setOnClickListener(new View.OnClickListener() {
@ -130,13 +130,13 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
private void initInterface() {
TextView tv_error = (TextView) findViewById(R.id.tv_error_default_smsapp);
TextView tv_error = findViewById(R.id.tv_error_default_smsapp);
tv_error.setText(R.string.error_make_default_sms_app);
findViewById(R.id.tv_restore_finished).setVisibility(View.INVISIBLE);
findViewById(R.id.tv_progress_value).setVisibility(View.INVISIBLE);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
ProgressBar pb = findViewById(R.id.progressbar_restore);
pb.setVisibility(View.INVISIBLE);
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
@ -158,10 +158,10 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
private void errorNotification(int err) {
TextView tv = (TextView) findViewById(R.id.tv_error_default_smsapp);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
ProgressBar pb = (ProgressBar) findViewById(R.id.progressbar_restore);
TextView tv = findViewById(R.id.tv_error_default_smsapp);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
ProgressBar pb = findViewById(R.id.progressbar_restore);
tv.setText(err);
tv.setVisibility(View.VISIBLE);
fix_button.setVisibility(View.INVISIBLE);
@ -182,9 +182,9 @@ public class RestoreMessagesActivity extends AppCompatActivity {
switch (requestCode) {
case RestoreMessagesActivity.REQUEST_DEFAULT_SMSAPP:
if (resultCode == Activity.RESULT_OK) {
TextView tv = (TextView) findViewById(R.id.tv_error_default_smsapp);
Button fix_button = (Button) findViewById(R.id.button_fix_permissions);
Button launch_restore = (Button) findViewById(R.id.button_launch_restore);
TextView tv = findViewById(R.id.tv_error_default_smsapp);
Button fix_button = findViewById(R.id.button_fix_permissions);
Button launch_restore = findViewById(R.id.button_launch_restore);
tv.setVisibility(View.INVISIBLE);
fix_button.setVisibility(View.INVISIBLE);
launch_restore.setVisibility(View.VISIBLE);
@ -225,7 +225,7 @@ public class RestoreMessagesActivity extends AppCompatActivity {
}
public void onProgressUpdate(Integer value) {
TextView tv_progress = (TextView) findViewById(R.id.tv_progress_value);
TextView tv_progress = findViewById(R.id.tv_progress_value);
if (tv_progress.getVisibility() == View.INVISIBLE) {
tv_progress.setVisibility(View.VISIBLE);
}

View File

@ -43,7 +43,7 @@ public class AndroidAccountAdapter extends ArrayAdapter<Account> {
final Account account = _accounts.get(position);
if (account != null) {
TextView label = (TextView) v.findViewById(AndroidAccountAdapter._accountFieldId);
TextView label = v.findViewById(AndroidAccountAdapter._accountFieldId);
if (label != null) {
label.setText(account.name + " >");
v.setOnClickListener(new OnClickListener() {

View File

@ -39,7 +39,7 @@ public class ContactListAdapter extends ArrayAdapter<String> {
String element = _objects.get(position);
if (element != null) {
TextView label = (TextView) v.findViewById(ContactListAdapter._fieldId);
TextView label = v.findViewById(ContactListAdapter._fieldId);
if (label != null) {
label.setText(element);
}

View File

@ -30,7 +30,7 @@ public class RecoveryPhoneNumberListViewAdapter extends ArrayAdapter<String> {
v = inflater.inflate(RecoveryPhoneNumberListViewAdapter._itemLayout, null);
}
TextView label = (TextView) v.findViewById(RecoveryPhoneNumberListViewAdapter._fieldId);
TextView label = v.findViewById(RecoveryPhoneNumberListViewAdapter._fieldId);
if (label != null) {
final String l = getItem(position).toString();
label.setText(getItem(position).toString());

View File

@ -32,9 +32,9 @@ import fr.unix_experience.owncloud_sms.engine.ASyncSMSSync;
import fr.unix_experience.owncloud_sms.engine.AndroidSmsFetcher;
import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.jni.SmsBuffer;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import ncsmsgo.SmsBuffer;
public class ConnectivityChanged extends BroadcastReceiver implements ASyncSMSSync {

View File

@ -28,9 +28,9 @@ import android.widget.Toast;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSMSNotificationType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.jni.SmsBuffer;
import fr.unix_experience.owncloud_sms.notifications.OCSMSNotificationUI;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
public interface ASyncSMSSync {
class SyncTask extends AsyncTask<Void, Void, Void> {

View File

@ -25,8 +25,8 @@ import org.json.JSONArray;
import org.json.JSONException;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.jni.SmsBuffer;
import fr.unix_experience.owncloud_sms.providers.SmsDataProvider;
import ncsmsgo.SmsBuffer;
public class AndroidSmsFetcher {
public AndroidSmsFetcher(Context ct) {
@ -53,7 +53,14 @@ public class AndroidSmsFetcher {
// Mailbox ID is required by server
entry.mailboxId = mbID.ordinal();
smsBuffer.push(mbID, entry);
smsBuffer.push(entry.id,
mbID.ordinal(),
entry.type,
entry.date,
entry.address,
entry.body,
entry.read ? "true" : "false",
entry.seen ? "true" : "false");
}
while (c.moveToNext());
}
@ -114,7 +121,14 @@ public class AndroidSmsFetcher {
* aren't indexed in the same mean
*/
entry.mailboxId = mboxId - 1;
results.push(mbID, entry);
results.push(entry.id,
mbID.ordinal(),
entry.type,
entry.date,
entry.address,
entry.body,
entry.read ? "true" : "false",
entry.seen ? "true" : "false");
c.close();

View File

@ -45,16 +45,12 @@ import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.providers.AndroidVersionProvider;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsHTTPClient;
import ncsmsgo.SmsPushResponse;
public class OCHttpClient {
static {
System.loadLibrary("nativesms");
}
public static native String getAllSmsIdsCall();
public static native String getLastMsgTimestamp();
public static native String getPushRoute();
public static native String getVersionCall();
private SmsHTTPClient _smsHttpClient;
private static final String TAG = OCHttpClient.class.getCanonicalName();
private static final String PARAM_PROTOCOL_VERSION = "http.protocol.version";
@ -63,12 +59,6 @@ public class OCHttpClient {
private final String _username;
private final String _password;
// API v2 calls
private static final String OC_V2_GET_PHONELIST = "/index.php/apps/ocsms/api/v2/phones/list?format=json";
private static final String OC_V2_GET_MESSAGES ="/index.php/apps/ocsms/api/v2/messages/[START]/[LIMIT]?format=json";
private static final String OC_V2_GET_MESSAGES_PHONE ="/index.php/apps/ocsms/api/v2/messages/[PHONENUMBER]/[START]/[LIMIT]?format=json";
private static final String OC_V2_GET_MESSAGES_SENDQUEUE = "/index.php/apps/ocsms/api/v2/messages/sendqueue?format=json";
public OCHttpClient(Context context, URL serverURL, String accountName, String accountPassword) {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
@ -97,6 +87,11 @@ public class OCHttpClient {
_username = accountName;
_password = accountPassword;
_smsHttpClient = new SmsHTTPClient();
// @TODO: at a point add a flag to permit insecure connections somewhere instead of trusting them
_smsHttpClient.init(_url.toString(), new AndroidVersionProvider(context).getVersionCode(),
_username, _password, false);
_userAgent = "nextcloud-phonesync (" + new AndroidVersionProvider(context).getVersionCode() + ")";
}
@ -127,23 +122,46 @@ public class OCHttpClient {
}
Pair<Integer, JSONObject> getAllSmsIds() throws OCSyncException {
return get(OCHttpClient.getAllSmsIdsCall(), false);
return get(_smsHttpClient.getAllSmsIdsCall(), false);
}
public Pair<Integer, JSONObject> getVersion() throws OCSyncException {
return get(OCHttpClient.getVersionCall(), true);
// Perform the GoLang doVersionCall and handle return
public Pair<Integer, Integer> getVersion() throws OCSyncException {
Integer serverAPIVersion = (int) _smsHttpClient.doVersionCall();
int httpStatus = (int) _smsHttpClient.getLastHTTPStatus();
// If last status is not 200, send the wrong status now
if (httpStatus != 200) {
return new Pair<>(httpStatus, 0);
}
if (serverAPIVersion > 0) {
return new Pair<>(200, serverAPIVersion);
}
else if (serverAPIVersion == 0) {
// Return default version
return new Pair<>(200, 1);
}
else if (serverAPIVersion == -1) {
// This return code from API means I/O error
throw new OCSyncException(R.string.err_sync_http_request_ioexception, OCSyncErrorType.IO);
}
else {
throw new OCSyncException(R.string.err_sync_http_request_returncode_unhandled, OCSyncErrorType.SERVER_ERROR);
}
}
Pair<Integer, JSONObject> pushSms(String smsBuf) throws OCSyncException {
return post(OCHttpClient.getPushRoute(), smsBuf);
Pair<Integer, SmsPushResponse> pushSms(SmsBuffer smsBuf) throws OCSyncException {
SmsPushResponse spr = _smsHttpClient.doPushCall(smsBuf);
return new Pair<>((int) _smsHttpClient.getLastHTTPStatus(), spr);
}
Pair<Integer, JSONObject> getPhoneList() throws OCSyncException {
return get(OCHttpClient.OC_V2_GET_PHONELIST, true);
return get(_smsHttpClient.getPhoneListCall(), true);
}
Pair<Integer, JSONObject> getMessages(Long start, Integer limit) throws OCSyncException {
return get(OCHttpClient.OC_V2_GET_MESSAGES
return get(_smsHttpClient.getMessagesCall()
.replace("[START]", start.toString())
.replace("[LIMIT]", limit.toString()), false);
}

View File

@ -33,8 +33,9 @@ import java.net.URL;
import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.enums.OCSyncErrorType;
import fr.unix_experience.owncloud_sms.exceptions.OCSyncException;
import fr.unix_experience.owncloud_sms.jni.SmsBuffer;
import fr.unix_experience.owncloud_sms.prefs.OCSMSSharedPrefs;
import ncsmsgo.SmsBuffer;
import ncsmsgo.SmsPushResponse;
@SuppressWarnings("deprecation")
public class OCSMSOwnCloudClient {
@ -56,6 +57,7 @@ public class OCSMSOwnCloudClient {
_http = new OCHttpClient(context,
serverURL, accountManager.getUserData(account, "ocLogin"),
accountManager.getPassword(account));
_connectivityMonitor = new ConnectivityMonitor(_context);
} catch (MalformedURLException e) {
throw new IllegalStateException(context.getString(R.string.err_sync_account_unparsable));
@ -63,21 +65,13 @@ public class OCSMSOwnCloudClient {
}
public Integer getServerAPIVersion() throws OCSyncException {
Pair<Integer, JSONObject> response = _http.getVersion();
if (response.second == null) {
// Return default version
return 1;
Pair<Integer, Integer> vPair = _http.getVersion();
_serverAPIVersion = vPair.second;
if (vPair.first == 200 && _serverAPIVersion > 0) {
return _serverAPIVersion;
}
try {
_serverAPIVersion = response.second.getInt("version");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "No version received from server, assuming version 1", e);
_serverAPIVersion = 1;
}
return _serverAPIVersion;
return 0;
}
JSONArray getServerPhoneNumbers() throws OCSyncException {
@ -111,7 +105,7 @@ public class OCSMSOwnCloudClient {
return;
}
// Create new JSONArray to get results
// Create new SmsBuffer to get results
smsBuffer = new SmsBuffer();
}
@ -120,28 +114,18 @@ public class OCSMSOwnCloudClient {
return;
}
Pair<Integer, JSONObject> response = _http.pushSms(smsBuffer.asRawJsonString());
Pair<Integer, SmsPushResponse> response = _http.pushSms(smsBuffer);
if (response.second == null) {
Log.e(OCSMSOwnCloudClient.TAG,"Request failed. It doesn't return a valid JSON Object");
Log.e(OCSMSOwnCloudClient.TAG,"Push request failed. GoLang response is empty.");
throw new OCSyncException(R.string.err_sync_push_request, OCSyncErrorType.IO);
}
Boolean pushStatus;
String pushMessage;
try {
pushStatus = response.second.getBoolean("status");
pushMessage = response.second.getString("msg");
}
catch (JSONException e) {
Log.e(OCSMSOwnCloudClient.TAG, "Invalid datas received from server", e);
throw new OCSyncException(R.string.err_sync_push_request_resp, OCSyncErrorType.PARSE);
}
// Push was OK, we can save the lastMessageDate which was saved to server
(new OCSMSSharedPrefs(_context)).setLastMessageDate(smsBuffer.getLastMessageDate());
Log.i(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " + pushStatus + " - " + pushMessage);
Log.i(OCSMSOwnCloudClient.TAG, "SMS Push request said: status " +
response.second.getStatus() + " - " + response.second.getMessage());
Log.i(OCSMSOwnCloudClient.TAG, "LastMessageDate set to: " + smsBuffer.getLastMessageDate());
}

View File

@ -1,69 +0,0 @@
package fr.unix_experience.owncloud_sms.jni;
import fr.unix_experience.owncloud_sms.engine.SmsEntry;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
/**
* Copyright (c) 2014-2017, Loic Blot <loic.blot@unix-experience.fr>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
public class SmsBuffer {
static {
System.loadLibrary("nativesms");
}
private long mHandle;
String TAG = SmsBuffer.class.getSimpleName();
public SmsBuffer() {
mHandle = SmsBuffer.createNativeObject();
}
protected void finalize() throws Throwable {
clear();
super.finalize();
}
private static native long createNativeObject();
private native void deleteNativeObject();
public native void push(int id, int mbid, int type, long date, String address,
String body, String read, String seen);
public native boolean empty();
public native void print();
public native String asRawJsonString();
public native long getLastMessageDate();
public void push(MailboxID mbid, SmsEntry smsEntry) {
push(smsEntry.id,
mbid.ordinal(),
smsEntry.type,
smsEntry.date,
smsEntry.address,
smsEntry.body,
smsEntry.read ? "true" : "false",
smsEntry.seen ? "true" : "false");
}
public void clear() {
if (mHandle == 0) {
return;
}
deleteNativeObject();
mHandle = 0;
}
}

View File

@ -32,8 +32,8 @@ import fr.unix_experience.owncloud_sms.engine.ConnectivityMonitor;
import fr.unix_experience.owncloud_sms.engine.OCSMSOwnCloudClient;
import fr.unix_experience.owncloud_sms.enums.MailboxID;
import fr.unix_experience.owncloud_sms.enums.PermissionID;
import fr.unix_experience.owncloud_sms.jni.SmsBuffer;
import fr.unix_experience.owncloud_sms.prefs.PermissionChecker;
import ncsmsgo.SmsBuffer;
public class SmsObserver extends ContentObserver implements ASyncSMSSync {