Дополненная реальность (Augmented Reality) не является какой-то новой технологией, но ее применение было замечено широкой публикой с появлением игры Pokemon GO, которая показала, что технология AR имеет большой потенциал. В книге рассмотрены различные способы разработки приложений с дополненной реальностью, от нативной разработки в Android Studio до использования таких движков, как Unity.

 

Wikitude
 
 
Wikitude SDK дополненной реальности сочетает в себе технологии 3D-трекинга (на основе Simultaneous Localisation and Mapping (SLAM)), распознавания образов, слежения и геолокации AR для мобильных устройств, планшетов и смарт-очков. Доступны также расширения для Unity, Cordova, Titanium и Xamarin.
После регистрации на сайте Wikitude (https://www.wikitude.com/) можно открыть веб приложение Wikitude Studio (http://studio.wikitude.com/dashboard).
Панель Wikitude Studio позволяет создать проект, который будет служить контейнером для целевых изображений, используемых в качестве эталона для распознавания образов, при их появлении в камере устройства.
После добавления целевого изображения в проект, выберете тип дополненной реальности:
Text – отображает текст поверх целевого изображения.
Image – отображает изображение поверх целевого изображения.
Button – отображает кнопку поверх целевого изображения, при нажатии на которую можно перейти по URL адресу.
HTML Widget – HTML код, отображаемый поверх целевого изображения.
3D Model – отображает 3D модель поверх целевого изображения. Предварительно 3D модель конвертируется из FBX (.fbx) формата в  Wikitude 3D Format (.wt3) с помощью Wikitude 3D Encoder.
Video – отображает видео поверх целевого изображения.
После выбора типа дополненной реальности и определения свойств объекта дополненной реальности, отображаемого поверх целевого изображения, нажмем кнопку Preview.
Установим на Android устройство приложение Wikitude App из Google Play.
Откроем приложение и в меню выберем Developer. Введем логин и пароль в Wikitude Studio. Выберем проект и наведем камеру устройства на целевое изображение – увидим дополненную реальность.
Нажмем кнопку Export в Wikitude Studio веб браузера. Здесь можно экспортировать приложение для доступа из Wikitude App и Wikitude Hosting, загрузить приложение для хранения на собственном сервере, а также загрузить проект приложения для разработки в среде IDE на основе Wikitude SDK.
При использовании Wikitude Hosting или собственного хостинга, URL адрес приложения вводится в приложении Wikitude App, в разделе Developer, в Wikitude Studio, для загрузки приложения.
Для разработки приложения в среде Android Studio, скачаем проект приложения на основе шаблона Android App Template и импортируем его в Android Studio.
После исправления ошибок в настройках проекта, приложение можно запустить на Android устройстве.
Скачаем Wikitude SDK (https://www.wikitude.com/download/) и получим лицензионный ключ, который вставим в protected static final String WIKITUDE_SDK_KEY = ""; файла WikitudeSDKConstants чтобы убрать водяные знаки из вида камеры. В результате все равно останется водяной знак Trial.
В папке libs проекта находится архивный файл Wikitude SDK. В папке assets проекта находится то самое приложение, которое скачивается при выборе Self-Hosting в Wikitude Studio веб браузера.
При запуске приложения сначала запускается активность RuntimePermissionRequestActivity, в которой запрашивается разрешение android.permission.CAMERA, после этого запускается активность SampleCamActivity, расширяющая класс AbstractArchitectCamActivity.
В классе SampleCamActivity в переопределенном методе getARchitectWorldPath возвращается путь к файлу index.html приложения папки assets. Если вы хотите загрузить приложение с сервера, в строке WORLD_PATH укажите URL адрес файла index.html.
В классе SampleCamActivity в переопределенном методе getWikitudeSDKLicenseKey возвращается лицензионный ключ, указанный в строке WikitudeSDKConstants.WIKITUDE_SDK_KEY.
В классе AbstractArchitectCamActivity создается экземпляр класса StartupConfiguration Wikitude SDK, в конструкторе которого указывается лицензионный ключ. Также создается основной вид приложения, представленный классом ArchitectView Wikitude SDK.
В классе StartupConfiguration лицензионный ключ передается в строковое поле класса.
package com.wikitude.architect;
 
import java.util.ArrayList;
 
public class StartupConfiguration {
private final String a;
private String b;
private final int c;
private final CameraPosition d;
public static final String ORIGIN_PHONEGAP = "CORDOVA";
public static final String ORIGIN_TITANIUM = "TITANIUM";
public static final String ORIGIN_XAMARIN = "XAMARIN";
public static final String ORIGIN_DEFAULT = "JAVASCRIPT_API";
 
protected String getOrigin() {
return this.a() ? this.b : "JAVASCRIPT_API";
}
 
private boolean a() {
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("CORDOVA");
arrayList.add("TITANIUM");
arrayList.add("XAMARIN");
arrayList.add("JAVASCRIPT_API");
return this.b != null && arrayList.contains(this.b);
}
 
public void setOrigin(String string) {
this.b = string;
}
 
public StartupConfiguration(String string) {
this(string, 3);
}
 
public StartupConfiguration(String string, int n2) {
this(string, n2, CameraPosition.DEFAULT);
}
 
public StartupConfiguration(String string, int n2, CameraPosition cameraPosition) {
this.a = string;
this.c = n2;
this.d = cameraPosition;
}
 
public String getKey() {
return this.a;
}
 
public int getFeatures() {
return this.c;
}
 
public CameraPosition getCameraMode() {
return this.d;
}
 
public static enum CameraPosition {
DEFAULT,
FRONT,
BACK;
 
 
private CameraPosition() {
}
}
 
public static interface Features {
public static final int Geo = 1;
public static final int Tracking2D = 2;
}
 
}
В классе ArchitectView в методе onCreate лицензионный ключ передается из класса StartupConfiguration в строковое поле класса ArchitectView и в методе onPostCreate вызывается нативный метод setKey(this.getContext().getPackageName(), startupConfiguration.getKey()). Далее имя пакета и лицензионный ключ сравниваются в нативной библиотеке папки jni Wikitude SDK.
package com.wikitude.architect;
 
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.Camera;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.util.JsonWriter;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.wikitude.architect.ArchitectWebView;
import com.wikitude.architect.BrowserActivity;
import com.wikitude.architect.CallbackHandler;
import com.wikitude.architect.Gameplay3dView;
import com.wikitude.architect.HtmlDrawableInterface;
import com.wikitude.architect.HtmlRenderManager;
import com.wikitude.architect.IArchitectCallbackListener;
import com.wikitude.architect.PlatformBridge;
import com.wikitude.architect.PlatformCamera;
import com.wikitude.architect.PluginManager;
import com.wikitude.architect.SensorService;
import com.wikitude.architect.ServiceManager;
import com.wikitude.architect.StartupConfiguration;
import com.wikitude.architect.plugin.Plugin;
import com.wikitude.tools.device.features.MissingDeviceFeatures;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.regex.Pattern;
 
public class ArchitectView
extends FrameLayout
implements e,
t {
static final String a = ArchitectView.class.getSimpleName();
private static final String i = "core";
private x j = null;
private PlatformCamera k = null;
private static final String l = "https://sdktracking.wikitude.com";
ArchitectWebView b;
private ViewState m;
Activity c;
static boolean d;
private ServiceManager n;
Gameplay3dView e;
private String o = "JAVASCRIPT_API";
private boolean p = false;
private AssetManager q;
private static final String r = "libarchitect.so";
private long nativePtr;
private ArchitectInitializeException s = null;
private static final Properties t;
private PlatformBridge u;
private HtmlDrawableInterface v;
private HtmlRenderManager w;
CallbackHandler f;
private m x;
private o y;
private StartupConfiguration z;
private StartupConfiguration.CameraPosition A;
private NetworkStateReceiver B;
private PluginManager C;
private List<Plugin> D = new ArrayList<Plugin>();
private String E;
public String g = "";
 
public ArchitectView(Context context, AttributeSet attributeSet, int n2) {
super(context, attributeSet, n2);
this.a(context);
}
 
public ArchitectView(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
}
 
public ArchitectView(Context context) {
super(context);
this.a(context);
}
 
@Override
public void onCameraOpen(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
this.j.a(camera);
this.j.setCameraParams(parameters);
this.k = new PlatformCamera(this.nativePtr);
this.j.setPlatformCamera(this.k);
this.e.a(parameters);
this.j.a((Object)parameters);
if (parameters == null) {
this.m = ViewState.initFailed;
throw new CamNotAccessibleException("Could not get camera parameters");
}
this.f.a(parameters.getHorizontalViewAngle(), parameters.getVerticalViewAngle());
}
 
@Override
public void onCameraReleased() {
}
 
@Override
public void onCameraOpenAbort() {
}
 
private boolean d() {
this.p = ArchitectView.e();
try {
if (t.getProperty("archive.type") != null && t.getProperty("archive.type").equals("jar")) {
PackageManager packageManager = this.getContext().getPackageManager();
String string = packageManager.getApplicationInfo((String)this.getContext().getPackageName(), (int)0).dataDir;
String string2 = "";
String string3 = System.getProperty("os.arch");
String string4 = string3.substring(0, 3).toUpperCase();
string2 = string4.equals("X86") || string4.equals("I68") ? "x86" : (string4.equals("AAR") ? "arm64-v8a" : (this.p ? "armeabi-v7a" : "armeabi"));
File file = new File(string, "libarchitect.so");
String string5 = "/libs/" + string2 + "/" + "libarchitect.so";
ArchitectView.a(file, string5);
try {
System.load(file.getAbsolutePath());
}
catch (UnsatisfiedLinkError var8_10) {
if (string2.equals("arm64-v8a")) {
string5 = "/libs/armeabi-v7a/libarchitect.so";
file.delete();
file = new File(string, "libarchitect.so");
ArchitectView.a(file, string5);
System.load(file.getAbsolutePath());
}
throw var8_10;
}
file.delete();
} else {
System.loadLibrary("architect");
}
d = true;
return true;
}
catch (IOException var1_2) {
var1_2.printStackTrace();
}
catch (PackageManager.NameNotFoundException var1_3) {
var1_3.printStackTrace();
}
return false;
}
 
private static void a(File file, String string) throws FileNotFoundException, IOException {
InputStream inputStream = ArchitectWebView.class.getResourceAsStream(string);
if (inputStream == null) {
return;
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
int n2 = 0;
byte[] arrby = new byte[8192];
while ((n2 = inputStream.read(arrby)) > -1) {
fileOutputStream.write(arrby, 0, n2);
}
fileOutputStream.close();
inputStream.close();
}
 
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
private static boolean e() {
boolean bl;
bl = false;
String string = System.getProperty("os.arch");
String string2 = string.substring(0, 3).toUpperCase();
if (string2.equals("X86") || string2.equals("I68") || string2.equals("AAR")) {
bl = true;
} else {
Scanner scanner = null;
try {
scanner = new Scanner(new FileInputStream("/proc/cpuinfo"));
while (scanner.hasNextLine()) {
if (!bl && scanner.findInLine("neon") != null) {
bl = true;
}
scanner.nextLine();
}
}
catch (Exception var4_4) {}
finally {
if (scanner != null) {
scanner.close();
}
}
}
return bl;
}
 
private void a(Context context) {
if (!this.d()) {
this.m = ViewState.initFailed;
this.s = new LibraryLoadFailedException("Architect native Library could not be loaded");
return;
}
this.c = (Activity)this.getContext();
this.e = new Gameplay3dView(context);
this.e.setZOrderMediaOverlay(true);
this.j = Build.VERSION.SDK_INT >= 21 ? new c(this, context) : (this.p && ArchitectView.g() ? new c(this, context) : new d(this, context));
this.n = new ServiceManager(this.c, this.j, this);
this.n.a(new OrientationEventListener(context){
 
public void onOrientationChanged(int n2) {
ArchitectView.this.e.b();
}
});
this.b = new ArchitectWebView(context, this);
this.f = new CallbackHandler(this.c, this);
this.u = new PlatformBridge((Context)this.c, this.b);
this.v = new HtmlDrawableInterface((Context)this.c, this.b);
this.b.addJavascriptInterface((Object)this.u, this.u.a());
this.b.addJavascriptInterface((Object)this.v, this.v.a());
this.b.enableJavascript();
this.y = new o((Activity)context, new r(), this.f);
this.e.setRenderListener(this.y);
this.x = new m((Activity)context, this.f);
FrameLayout frameLayout = new FrameLayout(context);
frameLayout.setLayoutParams((ViewGroup.LayoutParams)new FrameLayout.LayoutParams(-1, -1));
this.w = new HtmlRenderManager((Context)this.c, frameLayout, this.v);
this.f.a(this.n);
this.f.a(this.w);
this.f.a(this.y);
this.f.a(this.x);
this.setBackgroundColor(-16777216);
this.removeAllViews();
this.a((View)frameLayout);
this.a(this.j.getView());
this.j.addViewToLayout((View)this.e);
this.a((View)this.b);
this.createNative();
}
 
private static Properties f() {
Properties properties = new Properties();
try {
properties.load(ArchitectView.class.getClassLoader().getResourceAsStream("assets/sdk.properties"));
}
catch (Exception var1_1) {
Log.w((String)a, (String)"Cannot open properties file, use defaults");
}
return properties;
}
 
private static boolean g() {
boolean bl;
try {
File[] arrfile = new File("/sys/devices/system/cpu/").listFiles(new FileFilter(){
 
@Override
public boolean accept(File file) {
if (Pattern.matches("cpu[0-9]+", file.getName())) {
return true;
}
return false;
}
});
bl = arrfile.length > 1;
}
catch (Exception var1_1) {
bl = false;
}
return bl;
}
 
private void a(View view) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if (layoutParams == null && (layoutParams = this.generateDefaultLayoutParams()) == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
super.addView(view, -1, layoutParams);
}
 
public void setLocation(double d2, double d3, double d4, float f2) {
if (this.n != null && this.n.a() != null && this.n.a().h()) {
this.n.a().setLocation(d2, d3, d4, "FAKE", f2, System.currentTimeMillis());
}
}
 
public void setLocation(double d2, double d3, float f2) {
if (this.n != null && this.n.a() != null && this.n.a().h()) {
this.n.a().setLocationAltitudeUnknown(d2, d3, "FAKE", f2, System.currentTimeMillis());
}
}
 
public void registerUrlListener(ArchitectUrlListener architectUrlListener) {
if (this.b != null) {
this.b.registerUrlHandler(architectUrlListener);
}
}
 
public void registerWorldLoadedListener(ArchitectWorldLoadedListener architectWorldLoadedListener) {
if (this.b != null) {
this.b.registerWorldLoadedHandler(architectWorldLoadedListener);
}
}
 
public void onCreate(String string) {
this.onCreate(new StartupConfiguration(string));
}
 
public void onCreate(StartupConfiguration startupConfiguration) {
this.z = startupConfiguration;
this.o = startupConfiguration.getOrigin();
if (this.m == ViewState.initFailed) {
Log.e((String)a, (String)"delayed exception from constructor", (Throwable)this.s);
throw this.s;
}
if (startupConfiguration != null && startupConfiguration.getKey() != null) {
this.E = startupConfiguration.getKey();
int n2 = startupConfiguration.getFeatures();
if ((ArchitectView.getSupportedFeaturesForDevice(this.getContext()) & n2) != n2) {
throw new MissingFeatureException("Required feature set is not supported");
}
this.m = ViewState.constructed;
} else {
Log.e((String)a, (String)"App key not set. Switch to failsave state");
this.removeAllViews();
}
this.A = this.z.getCameraMode();
}
 
public StartupConfiguration.CameraPosition getCurrentCamera() {
return this.A;
}
 
public void setCameraPositionSimple(StartupConfiguration.CameraPosition cameraPosition) {
this.A = cameraPosition;
}
 
private long getTrackingId() {
String string = Settings.Secure.getString((ContentResolver)this.getContext().getContentResolver(), (String)"android_id");
if (string == null || string.trim().equals("")) {
string = Build.BRAND + Build.MODEL;
}
return ("wiki" + string + "tude").hashCode();
}
 
private void h() {
SharedPreferences sharedPreferences = this.getContext().getSharedPreferences("WTTRACKED", 0);
sharedPreferences.edit().putLong("value", this.getTrackingId()).commit();
}
 
private boolean i() {
SharedPreferences sharedPreferences = this.getContext().getSharedPreferences("WTTRACKED", 0);
return sharedPreferences.getLong("value", 0) == this.getTrackingId();
}
 
public void onPostCreate() {
if (this.m != ViewState.constructed) {
throw new IllegalStateException("This method only has to be called right after the object has been constructed");
}
this.m = ViewState.onPostCreate;
this.q = this.getResources().getAssets();
File file = new File(a.d(this.getContext()), "core");
if (!file.exists()) {
file.mkdir();
}
String string = ArchitectView.getBuildProperty("build.hardware");
String string2 = ArchitectView.getBuildProperty("cloud.tracker.url");
if (string != null) {
this.g = string;
}
this.createARchitectCore(this.f, this.q, file.getAbsolutePath(), this.g, this.z.getFeatures());
String string3 = this.getContext().getPackageName();
this.setKey(string3, this.E);
n.a(this.getContext(), this, this.getShowIcon(), this.getShowSplash());
if (this.A == StartupConfiguration.CameraPosition.FRONT) {
this.setCameraMirroring(true);
}
if (string2 != null) {
this.setCloudTrackerURL(string2);
}
if (this.getDoTracking() && !this.i()) {
String string4 = this.getSdkVersion();
String string5 = this.trackingOriginIdentifierFromString(this.o);
String string6 = System.getenv("XAMARIN_BUILD_ID");
if (string6 != null) {
string5 = this.trackingOriginIdentifierFromString("XAMARIN");
}
String string7 = this.trackingPlatformIdentifierFromString("android");
this.sendUsageTrackingRequest(string4, string5, string7);
String string8 = this.getLicenseType();
this.a(string8);
this.h();
}
this.j();
this.k();
this.e.c();
}
 
private void a(String string) {
if (string.equalsIgnoreCase("invalid")) {
((TextView)new AlertDialog.Builder((Context)this.c).setTitle((CharSequence)"License key is missing or invalid").setMessage((CharSequence)Html.fromHtml((String)"<p>Please add a valid license key to your app. <br><br> <a href=\"http://www.wikitude.com/external/doc/documentation/latest/android/triallicense.html#free-trial-license\">More information</a></p>")).show().findViewById(16908299)).setMovementMethod(LinkMovementMethod.getInstance());
Log.e((String)a, (String)"License key is missing or invalid. Please add a valid license key to your app.");
}
}
 
private void j() {
ConnectivityManager connectivityManager = (ConnectivityManager)this.getContext().getSystemService("connectivity");
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) {
this.setNetworkStatus(NetworkStatus.NONE.name());
} else if (networkInfo.getType() == 1) {
this.setNetworkStatus(NetworkStatus.WIFI.name());
} else {
this.setNetworkStatus(NetworkStatus.MOBILE.name());
}
this.B = new NetworkStateReceiver(this);
this.getContext().registerReceiver((BroadcastReceiver)this.B, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));
}
 
private void k() {
this.f.c(this.nativePtr);
this.e.a(this.nativePtr);
this.n.a(this.nativePtr);
this.u.connectNative(this.nativePtr);
this.v.connectNative(this.nativePtr);
}
 
public void onResume() {
if (this.m != ViewState.initFailed) {
this.m = ViewState.onResume;
this.j.onResume();
this.e.onResume();
this.n.c();
this.u.resume();
this.b.onResume();
this.x.resume();
this.y.resume();
} else {
Log.w((String)a, (String)"Can't resume activity, since intialization failed", (Throwable)this.s);
}
}
 
public void onPause() {
if (this.m != ViewState.onResume) {
throw new IllegalStateException("onResume() needs to be called before this method is called in the appropriate lifecycle method");
}
this.j.onPause();
this.m = ViewState.onPause;
this.u.pause();
this.b.onPause();
this.n.d();
this.x.pause();
this.y.pause();
this.e.onPause();
}
 
/*
* Unable to fully structure code
* Enabled aggressive block sorting
* Enabled unnecessary exception pruning
* Enabled aggressive exception aggregation
* Lifted jumps to return sites
*/
public void onDestroy() {
if (this.m != ViewState.initFailed) {
this.getContext().unregisterReceiver((BroadcastReceiver)this.B);
this.removeView((View)this.b);
if (this.C != null) {
this.C.destroyNative();
}
this.u.destroy();
this.v.onDestroy();
this.b.onDestroy();
this.n.e();
this.x.destroy();
this.y.destroy();
this.e.a();
if (this.k != null) {
this.k.a();
}
this.n = null;
var1_1 = new Thread(new Runnable(){
 
@Override
public void run() {
ArchitectView.this.destroyEngine();
ArchitectView.this.destroyNative();
}
});
try {
var1_1.start();
if (!var1_1.isAlive()) ** GOTO lbl25
var1_1.join();
}
catch (InterruptedException var2_2) {
throw new RuntimeException("Could not join pause/destroy thread, please check life-cycle calls");
}
} else {
Log.w((String)ArchitectView.a, (String)"Nothing was initialized, nothing will be destroyed", (Throwable)this.s);
}
lbl25: // 3 sources:
this.l();
}
 
private void l() {
try {
f.a(a.d((Context)this.c));
}
catch (Exception var1_1) {
Log.e((String)"Wikitude SDK onDestroy", (String)String.valueOf(var1_1.getMessage()));
}
}
 
public void callJavascript(String string) {
this.b.callJavaScript(string);
}
 
public void load(String string) throws IOException {
if (string.startsWith("http") || string.startsWith("file:")) {
this.b.loadArchitectFileFromUrl(string);
} else {
this.b.loadArchitectFileFromAssets(string);
}
}
 
public void a() {
}
 
void b() {
this.u.callSync("{\"is\":\"AR.i.contextInterface.destroyAll\"}");
}
 
public void onLowMemory() {
this.f.a();
}
 
public static String getCacheDirectoryAbsoluteFilePath(Context context) {
return ArchitectWebView.getCacheDirectoryRoot(context).getAbsolutePath();
}
 
public static int getSupportedFeaturesForDevice(Context context) {
boolean bl;
int n2 = 0;
if (Build.VERSION.SDK_INT >= 17) {
bl = context.getPackageManager().hasSystemFeature("android.hardware.camera.any");
} else {
boolean bl2 = bl = context.getPackageManager().hasSystemFeature("android.hardware.camera.front") || context.getPackageManager().hasSystemFeature("android.hardware.camera");
}
if (ArchitectView.b(context) >= 131072.0f && bl) {
if (ArchitectView.e()) {
n2 = 2;
}
LocationManager locationManager = (LocationManager)context.getSystemService("location");
SensorManager sensorManager = (SensorManager)context.getSystemService("sensor");
if (locationManager != null && locationManager.getAllProviders() != null && locationManager.getAllProviders().size() > 0 && sensorManager != null && sensorManager.getDefaultSensor(2) != null) {
n2 |= 1;
}
}
return n2;
}
 
public static boolean isDeviceSupported(Context context) {
return (ArchitectView.getSupportedFeaturesForDevice(context) & 3) == 3;
}
 
public static MissingDeviceFeatures isDeviceSupported(Context context, int n2) {
boolean bl;
String string = System.getProperty("line.separator");
String string2 = "";
if (Build.VERSION.SDK_INT >= 17) {
bl = context.getPackageManager().hasSystemFeature("android.hardware.camera.any");
} else {
boolean bl2 = bl = context.getPackageManager().hasSystemFeature("android.hardware.camera.front") || context.getPackageManager().hasSystemFeature("android.hardware.camera");
}
if (!bl) {
string2 = string2 + "- Camera" + string;
}
if (ArchitectView.b(context) < 131072.0f) {
string2 = string2 + "- OpenGLES version 2.0.+" + string;
}
if ((n2 & 1) == 1) {
LocationManager locationManager = (LocationManager)context.getSystemService("location");
SensorManager sensorManager = (SensorManager)context.getSystemService("sensor");
if (locationManager == null || locationManager.getAllProviders() == null || locationManager.getAllProviders().size() <= 0) {
string2 = string2 + "- GPS / Location Provider" + string;
}
if (sensorManager == null || sensorManager.getDefaultSensor(2) == null) {
string2 = string2 + "- Compass" + string;
}
}
if ((n2 & 2) == 2 && !ArchitectView.e()) {
string2 = string2 + "- Chipset supporting NEON" + string;
}
return new MissingDeviceFeatures(!string2.isEmpty(), "The device is missing following features:" + string + string2);
}
 
public String getSdkVersion() {
return d ? this.getArchitectVersion() : "N.A.";
}
 
public void setCullingDistance(float f2) {
if (this.e != null) {
this.e.setCullingDistance(f2);
}
}
 
public float getCullingDistance() {
return this.e != null ? this.e.getCullingDistance() : Float.MAX_VALUE;
}
 
public void clearAppCache() {
ArchitectWebView.clearCache((Context)this.c, 0);
}
 
private static float b(Context context) {
ActivityManager activityManager = (ActivityManager)context.getSystemService("activity");
ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
return configurationInfo.reqGlEsVersion;
}
 
private static boolean a(ApplicationInfo applicationInfo) {
return applicationInfo != null && 2 == (2 & applicationInfo.flags);
}
 
public void registerSensorAccuracyChangeListener(SensorAccuracyChangeListener sensorAccuracyChangeListener) {
this.n.a(sensorAccuracyChangeListener);
}
 
public void unregisterSensorAccuracyChangeListener(SensorAccuracyChangeListener sensorAccuracyChangeListener) {
this.n.b(sensorAccuracyChangeListener);
}
 
public void captureScreen(int n2, final FileOutputStream fileOutputStream) throws IllegalArgumentException {
int n3 = 100;
CaptureScreenCallback captureScreenCallback = new CaptureScreenCallback(){
 
@Override
public void onScreenCaptured(Bitmap bitmap) {
if (bitmap != null && ArchitectView.this.getContext() != null) {
bitmap.compress(Bitmap.CompressFormat.PNG, Math.max(0, Math.min(100, 100)), (OutputStream)fileOutputStream);
}
}
};
this.captureScreen(n2, captureScreenCallback);
}
 
public void captureScreen(int n2, final CaptureScreenCallback captureScreenCallback) throws IllegalArgumentException {
switch (n2) {
case 0: {
this.e.a(captureScreenCallback);
break;
}
case 1: {
CaptureScreenCallback captureScreenCallback2 = new CaptureScreenCallback(){
 
@Override
public void onScreenCaptured(final Bitmap bitmap) {
if (ArchitectView.this.b != null) {
ArchitectView.this.c.runOnUiThread(new Runnable(){
 
@Override
public void run() {
Bitmap bitmap3;
try {
bitmap3 = Bitmap.createBitmap((int)bitmap.getWidth(), (int)bitmap.getHeight(), (Bitmap.Config)bitmap.getConfig());
Canvas canvas = new Canvas(bitmap3);
canvas.drawBitmap(bitmap, new Matrix(), null);
Bitmap bitmap2 = Bitmap.createBitmap((int)ArchitectView.this.b.getWidth(), (int)ArchitectView.this.b.getHeight(), (Bitmap.Config)Bitmap.Config.ARGB_8888);
Canvas canvas2 = new Canvas(bitmap2);
ArchitectView.this.b.layout(0, 0, ArchitectView.this.b.getWidth(), ArchitectView.this.b.getHeight());
ArchitectView.this.b.draw(canvas2);
canvas.drawBitmap(bitmap2, new Matrix(), null);
}
catch (Exception var2_3) {
Log.w((String)ArchitectView.a, (String)"Can't capture screen - will return null", (Throwable)var2_3);
bitmap3 = null;
}
try {
captureScreenCallback.onScreenCaptured(bitmap3);
}
catch (Exception var2_4) {
Log.e((String)ArchitectView.a, (String)"Capture screen callback threw an exception", (Throwable)var2_4);
}
}
});
}
}
 
};
this.e.a(captureScreenCallback2);
break;
}
default: {
throw new IllegalArgumentException("captureMode must be listed in CaptureScreenCallback.CAPTURE_MODE_*");
}
}
}
 
public void setDisplayMode3dInCore(boolean bl) {
block4 : {
try {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter((Writer)stringWriter);
jsonWriter.beginObject();
if (bl) {
jsonWriter.name("3dmode").value("3d");
} else {
jsonWriter.name("3dmode").value("2d");
}
jsonWriter.endObject();
jsonWriter.close();
Log.i((String)"DEBUG", (String)("JSON:" + stringWriter.toString()));
this.setHardwareConfiguration(stringWriter.toString());
}
catch (IOException var2_3) {
if (h) break block4;
throw new AssertionError();
}
}
}
 
public void setFlashEnabled(boolean bl) {
this.j.setFlashEnabled(bl);
}
 
public static String getBuildProperty(String string) {
return t.getProperty(string);
}
 
void a(String string, boolean bl) {
if (string == null || string.length() < 4) {
Toast.makeText((Context)this.getContext(), (CharSequence)"invalid URL provided", (int)1).show();
return;
}
if (bl) {
Uri uri;
try {
new URL(string).getProtocol();
uri = Uri.parse((String)string);
}
catch (Exception var4_5) {
uri = Uri.parse((String)("http://" + string));
}
Intent intent = new Intent("android.intent.action.VIEW");
intent.setData(uri);
if (string.startsWith("file")) {
Intent intent2;
PackageManager packageManager = this.c.getPackageManager();
List list = packageManager.queryIntentActivities(intent2 = new Intent("android.intent.action.VIEW", Uri.parse((String)"http://")), 0);
if (!list.isEmpty()) {
packageManager.getLaunchIntentForPackage(((ResolveInfo)list.get((int)0)).activityInfo.packageName);
ComponentName componentName = new ComponentName(((ResolveInfo)list.get((int)0)).activityInfo.packageName, ((ResolveInfo)list.get((int)0)).activityInfo.name);
intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(componentName);
this.getContext().startActivity(intent);
}
} else {
this.getContext().startActivity(intent);
}
} else {
Intent intent = new Intent(this.getContext(), BrowserActivity.class);
intent.putExtra("URL", string);
this.getContext().startActivity(intent);
}
}
 
boolean c() {
return this.b.isActivityFinishing();
}
 
String getLastLoadedUrl() {
return this.b.getLastLoadedUrl();
}
 
void setCameraZoomLevel(float f2) {
this.j.setZoomLevel(f2);
Camera.Parameters parameters = this.j.getCameraParameter();
if (parameters.isZoomSupported()) {
int n2 = (Integer)parameters.getZoomRatios().get(parameters.getZoom());
float f3 = (float)Math.toDegrees(2.0 * Math.atan(100.0 * Math.tan(Math.toRadians(parameters.getHorizontalViewAngle()) / 2.0) / (double)n2));
float f4 = (float)Math.toDegrees(2.0 * Math.atan(100.0 * Math.tan(Math.toRadians(parameters.getVerticalViewAngle()) / 2.0) / (double)n2));
this.f.a(f3, f4);
} else {
this.f.a(parameters.getHorizontalViewAngle(), parameters.getVerticalViewAngle());
}
this.e.a(parameters);
this.j.setCameraParams(parameters);
}
 
void setCameraFocusMode(CameraFocusMode cameraFocusMode) {
this.j.setFocusMode(cameraFocusMode);
}
 
void setCameraPosition(StartupConfiguration.CameraPosition cameraPosition) {
this.A = cameraPosition;
this.j.f();
Camera.Parameters parameters = this.j.getCameraParameter();
if (parameters == null) {
this.m = ViewState.initFailed;
throw new CamNotAccessibleException("Could not get camera parameters");
}
this.f.a(parameters.getHorizontalViewAngle(), parameters.getVerticalViewAngle());
this.e.a(parameters);
this.j.setCameraParams(parameters);
}
 
public CameraFocusMode getCameraFocusMode() {
return this.j.getFocusMode();
}
 
public float getCameraZoomLevel() {
return this.j.getZoomLevel();
}
 
public float getCameraMaxZoomLevel() {
return this.j.getMaxZoomLevel();
}
 
public String[] getAvailableCameraFocusModes() {
return this.j.getAvailableCameraFocusModes();
}
 
public String[] getAvailableCameraPositions() {
return this.j.getAvailableCameraPositions();
}
 
public boolean registerPlugin(Plugin plugin) {
this.m();
if (this.C.registerPluginInCore(plugin)) {
this.D.add(plugin);
return true;
}
return false;
}
 
public boolean registerNativePlugins(String string) {
return this.registerNativePlugins(string, "");
}
 
public boolean registerNativePlugins(String string, String string2) {
if (this.m.ordinal() >= 1 && this.m != ViewState.initFailed) {
this.m();
try {
System.loadLibrary(string);
}
catch (UnsatisfiedLinkError var3_3) {
Log.e((String)a, (String)("Couldn't load plugins of library '" + string + "' because the library couldn't be loaded. Following error was thrown: " + var3_3.toString()));
return false;
}
long[] arrl = this.C.createNativePlugins(string2);
if (!this.C.registerNativePluginsInCore(arrl)) {
Log.e((String)a, (String)("A native plugin of library '" + string + "' couldn't be registered. All other plugins of this library were unregistered."));
return false;
}
} else {
Log.e((String)a, (String)("Registration of plugins in library '" + string + "' was canceled. Wrong lifecycle state detected."));
return false;
}
return true;
}
 
private void m() {
if (this.C == null) {
this.C = new PluginManager();
this.C.connectNative(this.nativePtr);
}
}
 
List<Plugin> getRegisteredPlugins() {
return this.D;
}
 
@Override
public native void createNative();
 
@Override
public native void destroyNative();
 
private native void createARchitectCore(IArchitectCallbackListener var1, AssetManager var2, String var3, String var4, int var5);
 
native void destroyEngine();
 
native void loadingFinished();
 
native String getArchitectVersion();
 
native void loadingStarted();
 
native void setNetworkStatus(String var1);
 
native void setCameraMirroring(boolean var1);
 
private native void setKey(String var1, String var2);
 
private native String getLicenseType();
 
private native String getCustomerMail();
 
private native boolean getShowIcon();
 
private native boolean getShowSplash();
 
private native boolean getDoTracking();
 
private native void setHardwareConfiguration(String var1);
 
private native void setCloudTrackerURL(String var1);
 
private native boolean sendUsageTrackingRequest(String var1, String var2, String var3);
 
private native String trackingPlatformIdentifierFromString(String var1);
 
private native String trackingOriginIdentifierFromString(String var1);
 
@Override
public long getNativePointer() {
return this.nativePtr;
}
 
static {
t = ArchitectView.f();
}
 
public static interface CaptureScreenCallback {
public static final int CAPTURE_MODE_CAM = 0;
public static final int CAPTURE_MODE_CAM_AND_WEBVIEW = 1;
 
public void onScreenCaptured(Bitmap var1);
}
 
public static interface ArchitectWorldLoadedListener {
public void worldWasLoaded(String var1);
 
public void worldLoadFailed(int var1, String var2, String var3);
}
 
public static interface ArchitectUrlListener {
public boolean urlWasInvoked(String var1);
}
 
public static interface SensorAccuracyChangeListener {
public void onCompassAccuracyChanged(int var1);
}
 
static class NetworkStateReceiver
extends BroadcastReceiver {
private static final String a = "NetworkStateReceiver";
private final ArchitectView b;
 
public NetworkStateReceiver(ArchitectView architectView) {
this.b = architectView;
}
 
public void onReceive(Context context, Intent intent) {
Log.d((String)"NetworkStateReceiver", (String)"Network connectivity change");
if (intent.getExtras() != null) {
ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService("connectivity");
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo == null || !networkInfo.isConnectedOrConnecting()) {
this.b.setNetworkStatus(NetworkStatus.NONE.name());
} else if (networkInfo.getType() == 1) {
this.b.setNetworkStatus(NetworkStatus.WIFI.name());
} else {
this.b.setNetworkStatus(NetworkStatus.MOBILE.name());
}
}
}
}
 
static enum NetworkStatus {
NONE,
WIFI,
MOBILE;
 
 
private NetworkStatus() {
}
}
 
public static enum CameraFocusMode {
ONCE,
CONTINUOUS;
 
 
private CameraFocusMode() {
}
}
 
static enum ViewState {
constructed,
onPostCreate,
onResume,
onPause,
onDestroy,
initFailed;
 
 
private ViewState() {
}
}
 
public static class LibraryLoadFailedException
extends ArchitectInitializeException {
private static final long serialVersionUID = -3436549205240611293L;
 
public LibraryLoadFailedException(String string) {
super(string);
}
}
 
public static class MissingFeatureException
extends ArchitectInitializeException {
private static final long serialVersionUID = -1120070352130259205L;
 
public MissingFeatureException(Exception exception) {
super(exception);
}
 
public MissingFeatureException(String string) {
super(string);
}
}
 
public static class CamNotAccessibleException
extends ArchitectInitializeException {
private static final long serialVersionUID = -2060234091280507469L;
 
public CamNotAccessibleException(Exception exception) {
super(exception);
}
 
public CamNotAccessibleException(String string) {
super(string);
}
}
 
public static abstract class ArchitectInitializeException
extends RuntimeException {
private static final long serialVersionUID = 3315635385062334407L;
 
public ArchitectInitializeException(Exception exception) {
super(exception);
}
 
public ArchitectInitializeException(String string) {
super(string);
}
}
 
}