Add login activity

This commit is contained in:
Gabriel Calero 2018-04-18 15:52:02 -03:00
parent a073cba4d4
commit 120de92c9e
23 changed files with 577 additions and 396 deletions

View file

@ -47,8 +47,9 @@
<activity
android:name=".GotoActivity"
android:label="@string/go_to"
android:theme="@style/AppTheme">
</activity>
android:theme="@style/AppTheme" />
<activity android:name=".LoginActivity"
android:theme="@style/AppTheme"/>
<activity
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
android:name=".InterfaceActivity"
@ -75,6 +76,10 @@
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
<activity
android:name=".SplashActivity"
android:theme="@style/Theme.AppCompat.Translucent.NoActionBar" />
</application>
<uses-feature android:name="android.software.vr.mode" android:required="true"/>

View file

@ -22,7 +22,9 @@
#include <AddressManager.h>
#include "AndroidHelper.h"
QAndroidJniObject __activity;
QAndroidJniObject __interfaceActivity;
QAndroidJniObject __loginActivity;
QAndroidJniObject __loadCompleteListener;
void tempMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
if (!message.isEmpty()) {
@ -142,16 +144,16 @@ void unpackAndroidAssets() {
extern "C" {
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) {
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
g_assetManager = AAssetManager_fromJava(env, asset_mgr);
__activity = QAndroidJniObject(instance);
qRegisterMetaType<QAndroidJniObject>("QAndroidJniObject");
__interfaceActivity = QAndroidJniObject(instance);
auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler);
unpackAndroidAssets();
qInstallMessageHandler(oldMessageHandler);
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [](const QString& a) {
QAndroidJniObject string = QAndroidJniObject::fromString(a);
__activity.callMethod<void>("openGotoActivity", "(Ljava/lang/String;)V", string.object<jstring>());
__interfaceActivity.callMethod<void>("openGotoActivity", "(Ljava/lang/String;)V", string.object<jstring>());
});
}
@ -167,15 +169,12 @@ JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUr
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnPause";
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnResume(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnResume";
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExitVr(JNIEnv* env, jobject obj) {
qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId();
}
JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGoBackFromAndroidActivity(JNIEnv *env, jobject instance) {
@ -204,4 +203,67 @@ JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_getCurren
return env->NewStringUTF(str.toLatin1().data());
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_LoginActivity_nativeLogin(JNIEnv *env, jobject instance,
jstring username_, jstring password_) {
const char *c_username = env->GetStringUTFChars(username_, 0);
const char *c_password = env->GetStringUTFChars(password_, 0);
QString username = QString(c_username);
QString password = QString(c_password);
env->ReleaseStringUTFChars(username_, c_username);
env->ReleaseStringUTFChars(password_, c_password);
QSharedPointer<AccountManager> accountManager = AndroidHelper::instance().getAccountManager();
__loginActivity = QAndroidJniObject(instance);
QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) {
AndroidHelper::instance().notifyLoginComplete(true);
});
QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() {
AndroidHelper::instance().notifyLoginComplete(false);
});
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::loginComplete, [](bool success) {
jboolean jSuccess = (jboolean) success;
__loginActivity.callMethod<void>("handleLoginCompleted", "(Z)V", jSuccess);
});
QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken", Q_ARG(const QString&, username), Q_ARG(const QString&, password));
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env,
jobject instance) {
__loadCompleteListener = QAndroidJniObject(instance);
QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() {
__loadCompleteListener.callMethod<void>("onAppLoadedComplete", "()V");
__interfaceActivity.callMethod<void>("onAppLoadedComplete", "()V");
QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr,
nullptr);
});
}
JNIEXPORT jboolean JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) {
return AndroidHelper::instance().getAccountManager()->isLoggedIn();
}
JNIEXPORT void JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeLogout(JNIEnv *env, jobject instance) {
AndroidHelper::instance().getAccountManager()->logout();
}
JNIEXPORT jstring JNICALL
Java_io_highfidelity_hifiinterface_HomeActivity_nativeGetDisplayName(JNIEnv *env,
jobject instance) {
QString username = AndroidHelper::instance().getAccountManager()->getAccountInfo().getUsername();
return env->NewStringUTF(username.toLatin1().data());
}
}

View file

@ -9,7 +9,6 @@ import android.support.v7.widget.Toolbar;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import java.net.URI;
@ -17,6 +16,8 @@ import java.net.URISyntaxException;
public class GotoActivity extends AppCompatActivity {
public static final String PARAM_DOMAIN_URL = "domain_url";
private EditText mUrlEditText;
private AppCompatButton mGoBtn;
@ -69,15 +70,10 @@ public class GotoActivity extends AppCompatActivity {
urlString = "hifi://" + urlString;
}
Intent intent = new Intent(this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, urlString);
Intent intent = new Intent();
intent.putExtra(GotoActivity.PARAM_DOMAIN_URL, urlString);
setResult(RESULT_OK, intent);
finish();
if (getIntent() != null &&
getIntent().hasExtra(HomeActivity.PARAM_NOT_START_INTERFACE_ACTIVITY) &&
getIntent().getBooleanExtra(HomeActivity.PARAM_NOT_START_INTERFACE_ACTIVITY, false)) {
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
}
startActivity(intent);
}
}

View file

@ -1,9 +1,7 @@
package io.highfidelity.hifiinterface;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
@ -22,18 +20,23 @@ import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import io.highfidelity.hifiinterface.QtPreloader.QtPreloader;
import io.highfidelity.hifiinterface.view.DomainAdapter;
public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public native boolean nativeIsLoggedIn();
public native void nativeLogout();
public native String nativeGetDisplayName();
/**
* Set this intent extra param to NOT start a new InterfaceActivity after a domain is selected"
*/
public static final String PARAM_NOT_START_INTERFACE_ACTIVITY = "not_start_interface_activity";
//public static final String PARAM_NOT_START_INTERFACE_ACTIVITY = "not_start_interface_activity";
public static final int ENTER_DOMAIN_URL = 1;
private DomainAdapter domainAdapter;
private DrawerLayout mDrawerLayout;
private ProgressDialog mDialog;
private NavigationView mNavigationView;
@Override
@ -89,15 +92,7 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
@Override
public void onItemClick(View view, int position, DomainAdapter.Domain domain) {
Intent intent = new Intent(HomeActivity.this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, domain.url);
HomeActivity.this.finish();
if (getIntent() != null &&
getIntent().hasExtra(PARAM_NOT_START_INTERFACE_ACTIVITY) &&
getIntent().getBooleanExtra(PARAM_NOT_START_INTERFACE_ACTIVITY, false)) {
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
}
startActivity(intent);
gotoDomain(domain.url);
}
});
domainsView.setAdapter(domainAdapter);
@ -112,49 +107,28 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
searchTextView.setTextAppearance(R.style.SearchText);
}
if (getIntent() == null ||
!getIntent().hasExtra(PARAM_NOT_START_INTERFACE_ACTIVITY) ||
!getIntent().getBooleanExtra(PARAM_NOT_START_INTERFACE_ACTIVITY, false)) {
preloadQt();
showActivityIndicator();
}
updateLoginMenu();
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(this);
}
mDialog.setMessage("Please wait...");
mDialog.setCancelable(false);
mDialog.show();
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
private void updateLoginMenu() {
TextView loginOption = findViewById(R.id.login);
TextView logoutOption = findViewById(R.id.logout);
if (nativeIsLoggedIn()) {
loginOption.setVisibility(View.GONE);
logoutOption.setVisibility(View.VISIBLE);
} else {
loginOption.setVisibility(View.VISIBLE);
logoutOption.setVisibility(View.GONE);
}
}
private AsyncTask preloadTask;
private void preloadQt() {
if (preloadTask == null) {
preloadTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
new QtPreloader(HomeActivity.this).initQt();
runOnUiThread(new Runnable() {
@Override
public void run() {
cancelActivityIndicator();
}
});
return null;
}
};
preloadTask.execute();
}
private void gotoDomain(String domainUrl) {
Intent intent = new Intent(HomeActivity.this, InterfaceActivity.class);
intent.putExtra(InterfaceActivity.DOMAIN_URL, domainUrl);
HomeActivity.this.finish();
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
@Override
@ -181,7 +155,6 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
@Override
protected void onDestroy() {
cancelActivityIndicator();
super.onDestroy();
}
@ -190,11 +163,33 @@ public class HomeActivity extends AppCompatActivity implements NavigationView.On
switch(item.getItemId()) {
case R.id.action_goto:
Intent i = new Intent(this, GotoActivity.class);
startActivity(i);
return true;
case R.id.action_settings:
startActivityForResult(i, ENTER_DOMAIN_URL);
return true;
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ENTER_DOMAIN_URL && resultCode == RESULT_OK) {
gotoDomain(data.getStringExtra(GotoActivity.PARAM_DOMAIN_URL));
}
}
@Override
protected void onStart() {
super.onStart();
updateLoginMenu();
}
public void onLoginClicked(View view) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
public void onLogoutClicked(View view) {
nativeLogout();
updateLoginMenu();
}
}

View file

@ -63,6 +63,7 @@ public class InterfaceActivity extends QtActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.isLoading = true;
Intent intent = getIntent();
if (intent.hasExtra(DOMAIN_URL) && !intent.getStringExtra(DOMAIN_URL).isEmpty()) {
intent.putExtra("applicationArguments", "--url "+intent.getStringExtra(DOMAIN_URL));
@ -112,6 +113,8 @@ public class InterfaceActivity extends QtActivity {
}
}
});
startActivity(new Intent(this, SplashActivity.class));
}
@Override
@ -201,7 +204,6 @@ public class InterfaceActivity extends QtActivity {
switch (activityName) {
case "Goto": {
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra(HomeActivity.PARAM_NOT_START_INTERFACE_ACTIVITY, true);
startActivity(intent);
break;
}
@ -212,4 +214,8 @@ public class InterfaceActivity extends QtActivity {
}
}
public void onAppLoadedComplete() {
super.isLoading = false;
}
}

View file

@ -0,0 +1,106 @@
package io.highfidelity.hifiinterface;
import android.app.ProgressDialog;
import android.support.annotation.MainThread;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class LoginActivity extends AppCompatActivity {
public native void nativeLogin(String username, String password);
private EditText mUsername;
private EditText mPassword;
private TextView mError;
private Button mLoginButton;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUsername = findViewById(R.id.username);
mPassword = findViewById(R.id.password);
mError = findViewById(R.id.error);
mLoginButton = findViewById(R.id.loginButton);
mPassword.setOnEditorActionListener(
new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
mLoginButton.performClick();
return true;
}
return false;
}
});
}
@Override
protected void onStop() {
super.onStop();
cancelActivityIndicator();
}
public void login(View view) {
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
if (username.isEmpty() || password.isEmpty()) {
showError(getString(R.string.login_username_or_password_incorrect));
} else {
mLoginButton.setEnabled(false);
hideError();
showActivityIndicator();
nativeLogin(username, password);
}
}
private void showActivityIndicator() {
if (mDialog == null) {
mDialog = new ProgressDialog(this);
}
mDialog.setMessage(getString(R.string.logging_in));
mDialog.setCancelable(false);
mDialog.show();
}
private void cancelActivityIndicator() {
if (mDialog != null) {
mDialog.cancel();
}
}
private void showError(String error) {
mError.setText(error);
mError.setVisibility(View.VISIBLE);
}
private void hideError() {
mError.setText("");
mError.setVisibility(View.INVISIBLE);
}
public void handleLoginCompleted(boolean success) {
runOnUiThread(() -> {
mLoginButton.setEnabled(true);
cancelActivityIndicator();
if (success) {
finish();
} else {
showError(getString(R.string.login_username_or_password_incorrect));
}
});
}
}

View file

@ -64,7 +64,7 @@ public class PermissionChecker extends Activity {
private void launchActivityWithPermissions(){
finish();
Intent i = new Intent(this, HomeActivity.class);
Intent i = new Intent(this, InterfaceActivity.class);
startActivity(i);
finish();
}

View file

@ -1,315 +0,0 @@
package io.highfidelity.hifiinterface.QtPreloader;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.util.Log;
import org.qtproject.qt5.android.bindings.QtApplication;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import dalvik.system.DexClassLoader;
/**
* Created by Gabriel Calero & Cristian Duarte on 3/22/18.
*/
public class QtPreloader {
public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
private ComponentInfo m_contextInfo;
private String[] m_qtLibs = null; // required qt libs
private Context m_context;
private static final String DEX_PATH_KEY = "dex.path";
private static final String LIB_PATH_KEY = "lib.path";
private static final String NATIVE_LIBRARIES_KEY = "native.libraries";
private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
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 int BUFFER_SIZE = 1024;
public QtPreloader(Context context) {
m_context = context;
}
public void initQt() {
try {
m_contextInfo = m_context.getPackageManager().getActivityInfo(new ComponentName("io.highfidelity.hifiinterface", "io.highfidelity.hifiinterface.InterfaceActivity"),
PackageManager.GET_META_DATA);
if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) {
int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id");
m_qtLibs = m_context.getResources().getStringArray(resourceId);
}
ArrayList<String> libraryList = new ArrayList<>();
String localPrefix = m_context.getApplicationInfo().dataDir + "/";
String pluginsPrefix = localPrefix + "qt-reserved-files/";
cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
extractBundledPluginsAndImports(pluginsPrefix);
for (String lib : m_qtLibs) {
libraryList.add(localPrefix + "lib/lib" + lib + ".so");
}
if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) {
String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":");
for (String lib : extraLibs) {
if (lib.length() > 0) {
if (lib.startsWith("lib/")) {
libraryList.add(localPrefix + lib);
} else {
libraryList.add(pluginsPrefix + lib);
}
}
}
}
Bundle loaderParams = new Bundle();
loaderParams.putString(DEX_PATH_KEY, new String());
loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES
+ "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml"
+ "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports"
+ "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins");
// add all bundled Qt libs to loader params
ArrayList<String> libs = new ArrayList<>();
String libName = m_contextInfo.metaData.getString("android.app.lib_name");
loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
// load and start QtLoader class
DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
m_context.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)
m_context.getClassLoader()); // parent loader
Class<?> loaderClass = classLoader.loadClass(loaderClassName()); // load QtLoader class
Object qtLoader = loaderClass.newInstance(); // create an instance
Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication",
contextClassName(),
ClassLoader.class,
Bundle.class);
prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams);
// now load the application library so it's accessible from this class loader
if (libName != null) {
System.loadLibrary(libName);
}
} catch (Exception e) {
Log.e(QtApplication.QtTAG, "Error pre-loading HiFi Qt app", e);
}
}
protected String loaderClassName() {
return "org.qtproject.qt5.android.QtActivityDelegate";
}
protected Class<?> contextClassName() {
return android.app.Activity.class;
}
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);
}
}
}
}
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 = m_context.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 {
String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/";
long packageVersion = -1;
try {
PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.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;
if (m_contextInfo.metaData.containsKey(key)) {
String[] list = m_context.getResources().getStringArray(m_contextInfo.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_contextInfo.metaData.containsKey(key)) {
String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key));
for (String fileName : list) {
String[] split = fileName.split(":");
String sourceFileName = split[0];
String destinationFileName = pluginsPrefix + split[1];
copyAsset(sourceFileName, destinationFileName);
}
}
}
}
}

View file

@ -0,0 +1,32 @@
package io.highfidelity.hifiinterface;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class SplashActivity extends Activity {
private native void registerLoadCompleteListener();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
registerLoadCompleteListener();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
public void onAppLoadedComplete() {
startActivity(new Intent(this, HomeActivity.class));
finish();
}
}

View file

@ -68,6 +68,8 @@ public class QtActivity extends Activity {
public final String QT_ANDROID_DEFAULT_THEME = QT_ANDROID_THEMES[0]; // sets the default theme.
private QtActivityLoader m_loader = new QtActivityLoader(this);
public boolean isLoading;
public QtActivity() {
}
@ -499,7 +501,11 @@ public class QtActivity extends Activity {
@Override
protected void onPause() {
super.onPause();
QtApplication.invokeDelegate();
// GC: this trick allow us to show a splash activity until Qt app finishes
// loading
if (!isLoading) {
QtApplication.invokeDelegate();
}
}
//---------------------------------------------------------------------------

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="350dp"
android:height="100dp"
android:viewportWidth="350"
android:viewportHeight="100">
<path
android:fillColor="#00B4F0"
android:pathData="M132.646,40.971 L132.646,62.955 L127.959,62.955 L127.959,53.361 L120.222,53.361 L120.222,62.955 L115.535,62.955 L115.535,40.971 L120.222,40.971 L120.222,49.453 L127.959,49.453 L127.959,40.971 L132.646,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M140.959,62.955 L136.271,62.955 L136.271,40.971 L140.959,40.971 L140.959,62.955 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M154.488,44.479 C153.383,44.479,152.454,44.568,151.658,44.79 C150.906,45.012,150.287,45.367,149.801,45.856 C149.315,46.345,149.006,47.055,148.784,47.899 C148.563,48.743,148.474,49.809,148.474,51.053 L148.474,53.051 C148.474,54.383,148.563,55.449,148.74,56.293 C148.917,57.137,149.226,57.803,149.668,58.247 C150.066,58.736,150.641,59.047,151.305,59.225 C151.968,59.403,152.808,59.492,153.781,59.492 C154.047,59.492,154.356,59.492,154.665,59.448 C154.976,59.448,155.284,59.403,155.638,59.358 L155.638,53.408 L152.898,53.408 L153.339,49.765 L160.06,49.765 L160.06,62.29 C159.22,62.601,158.16,62.823,156.876,63.001 C155.638,63.179,154.312,63.267,152.985,63.267 C151.305,63.267,149.89,63.045,148.741,62.646 C147.548,62.247,146.619,61.625,145.867,60.782 C145.116,59.937,144.585,58.916,144.231,57.628 C143.877,56.385,143.745,54.874,143.745,53.188 L143.745,50.921 C143.745,49.367,143.922,47.99,144.231,46.702 C144.585,45.414,145.16,44.348,145.956,43.46 C146.751,42.572,147.812,41.861,149.095,41.373 C150.377,40.884,152.013,40.618,153.958,40.618 C155.02,40.618,156.081,40.706,157.097,40.884 C158.115,41.062,158.954,41.284,159.618,41.506 L158.866,45.059 C158.291,44.881,157.628,44.748,156.921,44.615 C156.212,44.523,155.416,44.479,154.488,44.479 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M181.104,40.971 L181.104,62.955 L176.417,62.955 L176.417,53.361 L168.724,53.361 L168.724,62.955 L164.037,62.955 L164.037,40.971 L168.724,40.971 L168.724,49.453 L176.461,49.453 L176.461,40.971 L181.104,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M206.084,40.971 L205.555,44.79 L197.375,44.79 L197.375,49.365 L204.936,49.365 L204.405,53.183 L197.377,53.183 L197.377,62.954 L192.689,62.954 L192.689,40.971 L206.084,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M213.688,62.955 L209.001,62.955 L209.001,40.971 L213.688,40.971 L213.688,62.955 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M217.447,40.971 L224.609,40.971 C226.465,40.971,228.013,41.237,229.252,41.726 C230.491,42.215,231.463,42.925,232.215,43.769 C232.966,44.657,233.452,45.679,233.762,46.922 C234.073,48.166,234.203,49.498,234.203,50.92 L234.203,53.007 C234.203,54.473,234.071,55.805,233.762,57.049 C233.452,58.292,232.922,59.315,232.215,60.202 C231.463,61.09,230.49,61.756,229.252,62.245 C228.014,62.732,226.467,63,224.609,63 L217.491,63 L217.491,40.971 L217.447,40.971 L217.447,40.971 Z M222.09,59.137 L224.123,59.137 C225.008,59.137,225.76,59.047,226.467,58.825 C227.13,58.603,227.705,58.248,228.147,57.76 C228.589,57.272,228.899,56.606,229.121,55.805 C229.342,55.006,229.431,53.985,229.431,52.742 L229.431,51.098 C229.431,49.854,229.342,48.832,229.121,48.034 C228.899,47.235,228.591,46.569,228.147,46.08 C227.707,45.591,227.174,45.236,226.467,45.059 C225.803,44.881,225.008,44.748,224.123,44.748 L222.09,44.748 L222.09,59.137 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M250.871,40.971 L250.385,44.657 L241.942,44.657 L241.942,49.453 L249.899,49.453 L249.459,53.141 L241.985,53.141 L241.985,59.315 L250.918,59.315 L250.43,63 L237.254,63 L237.254,40.971 L250.871,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M258.609,40.971 L258.609,59.137 L267.496,59.137 L266.965,62.955 L253.922,62.955 L253.922,40.971 L258.609,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M275.232,62.955 L270.544,62.955 L270.544,40.971 L275.232,40.971 L275.232,62.955 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M295.482,40.971 L294.952,44.79 L289.027,44.79 L289.027,62.955 L284.339,62.955 L284.339,44.79 L277.884,44.79 L278.415,40.971 L295.482,40.971 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M306.58,49.543 L311.134,40.971 L315.953,40.971 L308.791,53.363 L308.791,62.956 L304.103,62.956 L304.103,53.363 L297.03,40.971 L302.159,40.971 L306.58,49.543 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M68.848,83.209 C64.427,83.209,60.094,82.32,56.07,80.633 C52.135,78.989,48.642,76.591,45.636,73.571 C42.63,70.551,40.242,66.999,38.606,63.091 C36.837,58.96,35.953,54.653,35.953,50.166 C35.953,45.725,36.837,41.372,38.517,37.33 C40.153,33.377,42.54,29.869,45.547,26.848 C48.553,23.828,52.09,21.43,55.981,19.786 C60.049,18.054,64.337,17.21,68.758,17.21 C73.179,17.21,77.513,18.098,81.536,19.786 C85.471,21.429,88.964,23.828,91.97,26.848 C94.977,29.869,97.365,33.422,98.999,37.33 C100.724,41.416,101.563,45.725,101.563,50.166 C101.563,54.606,100.681,58.959,99,63.001 C97.365,66.954,94.978,70.462,91.971,73.483 C88.965,76.503,85.428,78.901,81.537,80.544 C77.602,82.32,73.269,83.209,68.848,83.209 Z M68.848,20.584 C52.621,20.584,39.402,33.864,39.402,50.164 C39.402,66.465,52.621,79.744,68.848,79.744 S98.293,66.465,98.293,50.164 C98.293,33.864,85.074,20.584,68.848,20.584 Z" />
<path
android:fillColor="#00B4F0"
android:pathData="M78.884,64.199 L78.884,41.014 C80.343,40.525,81.36,39.149,81.36,37.55 C81.36,35.551,79.725,33.908,77.735,33.908 C75.745,33.908,74.11,35.552,74.11,37.55 C74.11,39.105,75.038,40.393,76.409,40.97 L76.409,51.762 L61.376,44.745 L61.376,36.217 C62.835,35.729,63.852,34.352,63.852,32.753 C63.852,30.754,62.216,29.111,60.227,29.111 C58.238,29.111,56.602,30.755,56.602,32.753 C56.602,34.308,57.531,35.596,58.901,36.173 L58.901,59.491 C57.574,60.023,56.602,61.355,56.602,62.911 C56.602,64.91,58.238,66.553,60.227,66.553 C62.216,66.553,63.852,64.91,63.852,62.911 C63.852,61.312,62.835,59.935,61.376,59.447 L61.376,47.676 L76.409,54.694 L76.409,64.244 C75.083,64.775,74.11,66.109,74.11,67.663 C74.11,69.662,75.745,71.305,77.735,71.305 C79.725,71.305,81.36,69.662,81.36,67.663 C81.359,66.02,80.343,64.688,78.884,64.199 Z" />
</vector>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton1" />
<solid android:color="@color/colorButton1"/>
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton1" />
<solid android:color="@color/colorButton1"/>
</shape>
</item>
<item>
<shape android:shape="rectangle" >
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@color/colorButton1" />
<solid android:color="@color/colorButton1"/>
</shape>
</item>
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="9dp">
<corners android:radius="4dip" />
<stroke android:width="1dip" android:color="@android:color/black" />
<solid android:color="@color/backgroundEditText"/>
</shape>

View file

@ -3,8 +3,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_activity_goto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context="io.highfidelity.hifiinterface.GotoActivity">
<android.support.v7.widget.Toolbar

View file

@ -33,6 +33,29 @@
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/menu_home"
/>
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:clickable="true"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin">
<TextView
android:id="@+id/login"
android:text="@string/login"
android:onClick="onLoginClicked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/menuOption" />
<TextView
android:id="@+id/logout"
android:text="@string/logout"
android:onClick="onLogoutClicked"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/menuOption" />
</LinearLayout>
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>

View file

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLight"
tools:context="io.highfidelity.hifiinterface.LoginActivity">
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/hifi_header"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/username"
android:layout_marginBottom="75dp"
/>
<TextView
android:id="@+id/error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:fontFamily="@font/raleway"
android:textColor="@color/colorLoginError"
android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@id/username"
app:layout_constraintLeft_toLeftOf="@id/username"
android:visibility="invisible"/>
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:background="@drawable/rounded_edit"
android:padding="9dp"
android:paddingRight="12dp"
android:ems="10"
android:fontFamily="@font/raleway"
android:inputType="textEmailAddress"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="right"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:hint="@string/username_or_email" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:background="@drawable/rounded_edit"
android:padding="9dp"
android:paddingRight="12dp"
android:ems="10"
android:fontFamily="@font/raleway"
android:inputType="textPassword"
android:textStyle="italic"
android:textColor="@color/editTextColor"
android:textColorHint="@color/editTextColor"
android:gravity="right"
android:layout_marginTop="14dp"
app:layout_constraintTop_toBottomOf="@id/username"
android:hint="@string/password"
android:imeOptions="actionDone"/>
<Button
android:id="@+id/loginButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_button"
android:textColor="@color/white_opaque"
android:text="@string/login"
app:layout_constraintRight_toRightOf="@id/username"
app:layout_constraintTop_toBottomOf="@id/password"
android:paddingRight="30dp"
android:paddingLeft="30dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_marginTop="16dp"
android:onClick="login"/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:fontFamily="@font/raleway_semibold"
android:text="@string/forgot_password"
android:textStyle="italic"
android:padding="10dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/loginButton"
android:textColor="@color/colorButton1"/>
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_activity_splash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<!-- TODO -->
</RelativeLayout>

View file

@ -4,8 +4,13 @@
<color name="colorPrimary">#272727</color>
<color name="colorPrimaryDark">#000000</color>
<color name="colorAccent">#54D7FD</color>
<color name="backgroundEditText">#E3E3E3</color>
<color name="editTextColor">#575757</color>
<color name="tabs">#1EB5EC</color>
<color name="colorButton1">#00B4EF</color>
<color name="backgroundDark">#333333</color>
<color name="backgroundLight">#4F4F4F</color>
<color name="backgroundSearch">#33999999</color>
<color name="menuOption">#F2F2F2</color>
<color name="colorLoginError">#FF7171</color>
</resources>

View file

@ -13,5 +13,12 @@
<string name="action_goto">Go To</string>
<string name="goto_url_hint">Type a domain url</string>
<string name="go">Go</string>
<string name="username_or_email">Username or email</string>
<string name="password">Password</string>
<string name="login">Login</string>
<string name="logout">Logout</string>
<string name="forgot_password">Forgot password?</string>
<string name="login_username_or_password_incorrect">Username or password incorrect.</string>
<string name="logging_in">Logging into High Fidelity</string>
</resources>

View file

@ -7,7 +7,14 @@
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="Theme.AppCompat.Translucent.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowActionBar">false</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>

View file

@ -11,10 +11,39 @@
#include "AndroidHelper.h"
#include <QDebug>
AndroidHelper::AndroidHelper() :
_accountManager ()
{
workerThread.start();
}
AndroidHelper::~AndroidHelper() {
workerThread.quit();
workerThread.wait();
}
QSharedPointer<AccountManager> AndroidHelper::getAccountManager() {
_accountManager = QSharedPointer<AccountManager>(new AccountManager, &QObject::deleteLater);
_accountManager->setIsAgent(true);
_accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL());
_accountManager->moveToThread(&workerThread);
return _accountManager;
}
void AndroidHelper::requestActivity(const QString &activityName) {
emit androidActivityRequested(activityName);
}
void AndroidHelper::notifyLoadComplete() {
emit qtAppLoadComplete();
}
void AndroidHelper::goBackFromAndroidActivity() {
emit backFromAndroidActivity();
}
void AndroidHelper::notifyLoginComplete(bool success) {
emit loginComplete(success);
}

View file

@ -13,6 +13,9 @@
#define hifi_Android_Helper_h
#include <QObject>
#include <QThread>
#include <AccountManager.h>
#include <QtAndroidExtras/QAndroidJniObject>
class AndroidHelper : public QObject {
Q_OBJECT
@ -22,16 +25,27 @@ public:
return instance;
}
void requestActivity(const QString &activityName);
void notifyLoadComplete();
void goBackFromAndroidActivity();
void notifyLoginComplete(bool success);
QSharedPointer<AccountManager> getAccountManager();
AndroidHelper(AndroidHelper const&) = delete;
void operator=(AndroidHelper const&) = delete;
signals:
void androidActivityRequested(const QString &activityName);
void backFromAndroidActivity();
void qtAppLoadComplete();
void loginComplete(bool success);
private:
AndroidHelper() {}
AndroidHelper();
~AndroidHelper();
QSharedPointer<AccountManager> _accountManager;
QThread workerThread;
};
#endif

View file

@ -2117,6 +2117,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_pendingRenderEvent = false;
qCDebug(interfaceapp) << "Metaverse session ID is" << uuidStringWithoutCurlyBraces(accountManager->getSessionID());
#if defined(Q_OS_ANDROID)
AndroidHelper::instance().notifyLoadComplete();
#endif
}
void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) {