Java tutorial
package com.takondi.tartt; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Toast; import com.takondi.tartt.helper.Const; import com.takondi.tartt.helper.GuiHelper; import com.takondi.tartt.helper.GuiHelper.GUI_STATE; import com.takondi.tartt.zxing.QrCodeResultListener; import com.takondi.tartt.zxing.SampleZXingPlugin; import com.takondi.tarttsdk.ChannelManager; import com.takondi.tarttsdk.Tartt; import com.takondi.tarttsdk.model.Channel; import com.takondi.tarttsdk.model.TarttRequestOptions; import com.takondi.tarttsdk.utils.Utils; import com.wikitude.architect.ArchitectJavaScriptInterfaceListener; import com.wikitude.architect.ArchitectStartupConfiguration; import com.wikitude.architect.ArchitectView; import com.wikitude.common.plugins.PluginManager; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.IOException; public class ARActivity extends AppCompatActivity implements ArchitectView.ArchitectUrlListener, ArchitectJavaScriptInterfaceListener, ArchitectView.ArchitectWorldLoadedListener, ChannelManager.Callback, QrCodeResultListener { private final static String TAG = ARActivity.class.getSimpleName(); private final static String ARG_INIT_WITH_QR = "arg_init_qr"; private static final String LANG_FALLBACK = "de"; private static final String JS_START_EXPERIENCE = "startExperience"; private static final String JS_BARCODE_RECEIVED = "performBarcodeRequest"; private static final String JS_GET_USER_DEFAULTS_RESULT = "getUserDefaultsResult"; private static final String JS_SET_USER_DEFAULTS_RESULT = "setUserDefaultsResult"; private static final String JS_GET_USER_DEFAULTS_BY_KEY_RESULT = "getUserDefaultsByKeyResult"; private static final String JS_RESET_USER_DEFAULTS_RESULT = "resetUserDefaultsResult"; private static final String JS_DESTROY = "AR.context.destroyAll()"; private static final String URL_ARCHITECTSDK_SCHEME = "architectsdk"; private static final String METHOD_TARGET_LOADED = "targetsLoaded"; private static final String METHOD_ENTER_VISION = "augmentationsOnEnterFieldOfVision"; private static final String METHOD_EXIT_VISION = "augmentationsOnExitFieldOfVision"; private static final String METHOD_READY_FOR_EXECUTION = "readyForExecution"; private static final String METHOD_QR_TRIGGER = "qrCodeTrigger"; private static final String METHOD_CHANGE_CHANNEL = "changeChannel"; private static final String METHOD_GET_USER_DEFAULT_BY_KEY = "getUserDefaultsByKey"; private static final String METHOD_GET_USER_DEFAULT = "getUserDefaults"; private static final String METHOD_SET_USER_DEFAULT = "setUserDefaults"; private static final String METHOD_RESET_USER_DEFAULT = "resetUserDefaults"; private static final String QR_TARTT_SCHEME = "tartt"; private static final String JSON_METHOD = "method"; private static final String JSON_PARAMS = "parameters"; private static final String QR_CHANNEL_HOST = "channelCode"; private ArchitectView mArchitectView; private SampleZXingPlugin mQrPlugin; private long mBytesToLoad; private GuiHelper mGuiHelper; private ChannelManager mChannelManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (areAnyRequiredPermissionsMissing()) { Intent intent = new Intent(this, PermissionActivity.class); startActivity(intent); finish(); return; } setContentView(R.layout.activity_ar); mGuiHelper = new GuiHelper(this); mChannelManager = new ChannelManager(this); mArchitectView = (ArchitectView) findViewById(R.id.architectView); mGuiHelper.mSwitchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { switchChannelRequested(); } }); mGuiHelper.mRetryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { doInitialLoad(); } }); mGuiHelper.mCancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mChannelManager.cancel(); mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); mQrPlugin.activate(); } }); final ArchitectStartupConfiguration config = new ArchitectStartupConfiguration(); config.setLicenseKey(Const.WIKITUDE_KEY); config.setFeatures(ArchitectStartupConfiguration.Features.ImageTracking); try { mArchitectView.onCreate(config); } catch (RuntimeException e) { e.printStackTrace(); mArchitectView = null; } } private boolean areAnyRequiredPermissionsMissing() { return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; } @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (this.mArchitectView != null) { mArchitectView.onPostCreate(); mQrPlugin = new SampleZXingPlugin("com.plugin.zxing", this); mArchitectView.registerPlugin(mQrPlugin, new PluginManager.PluginErrorCallback() { @Override public void onRegisterError(int i, String s) { Toast.makeText(ARActivity.this, "The barcode scanner plugin could not be loaded.", Toast.LENGTH_SHORT).show(); } }); mArchitectView.registerWorldLoadedListener(this); mArchitectView.addArchitectJavaScriptInterfaceListener(this); mArchitectView.registerUrlListener(this); if (!ArchitectView.isDeviceSupported(ARActivity.this)) { mGuiHelper.changeGuiState(GUI_STATE.HIDE); Toast.makeText(ARActivity.this, "Device is not supported", Toast.LENGTH_SHORT).show(); } else if (getIntent() != null && getIntent().hasExtra(ARG_INIT_WITH_QR)) { mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); loadDefaultWorld(); mArchitectView.post(new Runnable() { @Override public void run() { onQrCodeRead(getIntent().getStringExtra(ARG_INIT_WITH_QR)); } }); } else { doInitialLoad(); } } } private void doInitialLoad() { mGuiHelper.changeGuiState(GUI_STATE.INITIAL_LOADING); mChannelManager.findChannels(this); } private void loadDefaultWorld() { mArchitectView.callJavascript(JS_DESTROY); try { String url = Const.DUMMY_WORLD_URL; mArchitectView.load(url); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onStart() { super.onStart(); if (mGuiHelper.mGuiState == GUI_STATE.SCAN_QR || mGuiHelper.mGuiState == GUI_STATE.SCAN) { if (mQrPlugin != null) { mQrPlugin.activate(); } } } @Override protected void onResume() { super.onResume(); if (this.mArchitectView != null) { mArchitectView.onResume(); } } @Override protected void onPause() { super.onPause(); if (this.mArchitectView != null) { mArchitectView.onPause(); } } @Override protected void onStop() { super.onStop(); if (mQrPlugin != null) { mQrPlugin.deactivate(); } mChannelManager.cancel(); } @Override protected void onDestroy() { if (this.mArchitectView != null) { mArchitectView.onDestroy(); } super.onDestroy(); } @Override public void onLowMemory() { super.onLowMemory(); if (mArchitectView != null) { mArchitectView.onLowMemory(); } } @Override public void worldWasLoaded(String s) { Log.d(TAG, "worldWasLoaded " + s); } @Override public boolean urlWasInvoked(String url) { Log.d(TAG, "urlWasInvoked " + url); if (url != null) { Uri uri = Uri.parse(url); if (URL_ARCHITECTSDK_SCHEME.equals(uri.getScheme())) { JSONObject paramJson = new JSONObject(); if (!uri.getQueryParameterNames().isEmpty()) { for (String param : uri.getQueryParameterNames()) { try { paramJson.put(param, uri.getQueryParameter(param)); } catch (JSONException e) { e.printStackTrace(); } } } handleEvent(uri.getHost(), paramJson); } } return false; } @Override public void onJSONObjectReceived(final JSONObject jsonObject) { Log.d(TAG, "onJSONObjectReceived " + jsonObject.toString()); if (jsonObject.has(JSON_METHOD) && jsonObject.has(JSON_PARAMS)) { String methodName = jsonObject.optString(JSON_METHOD); JSONObject paramsJson = jsonObject.optJSONObject(JSON_PARAMS); handleEvent(methodName, paramsJson); } } private void handleEvent(final String methodName, final JSONObject paramJson) { runOnUiThread(new Runnable() { @Override public void run() { switch (methodName) { case METHOD_READY_FOR_EXECUTION: mArchitectView .callJavascript(JS_START_EXPERIENCE + "(" + Utils.getStartExperienceJson(ARActivity.this, PreferenceManager.getInstance(ARActivity.this).getDefaults(null)) + ")"); break; case METHOD_TARGET_LOADED: mGuiHelper.changeGuiState(GUI_STATE.SCAN); mQrPlugin.activate(); break; case METHOD_ENTER_VISION: mGuiHelper.changeGuiState(GUI_STATE.HIDE); mQrPlugin.deactivate(); break; case METHOD_EXIT_VISION: mGuiHelper.changeGuiState(GUI_STATE.SCAN); mQrPlugin.activate(); break; case METHOD_QR_TRIGGER: processQr(paramJson.optString("code")); break; case METHOD_CHANGE_CHANNEL: switchChannelRequested(); break; case METHOD_GET_USER_DEFAULT_BY_KEY: String key = paramJson.optString("key"); String value = PreferenceManager.getInstance(ARActivity.this).getDefault(key); String taskId = paramJson.optString("taskid"); taskId = "".equals(taskId) ? null : taskId; try { JSONObject obj = new JSONObject(); obj.put("key", key); obj.put("value", value == null ? JSONObject.NULL : value); obj.put("taskid", taskId); mArchitectView .callJavascript(JS_GET_USER_DEFAULTS_BY_KEY_RESULT + "(" + obj.toString() + ")"); } catch (JSONException e) { e.printStackTrace(); } break; case METHOD_GET_USER_DEFAULT: String prefix = paramJson.optString("prefix"); prefix = "".equals(prefix) ? null : prefix; String taskIdGetDefaults = paramJson.optString("taskid"); taskIdGetDefaults = "".equals(taskIdGetDefaults) ? null : taskIdGetDefaults; try { JSONObject obj = PreferenceManager.getInstance(ARActivity.this).getDefaults(prefix); obj.put("taskid", taskIdGetDefaults); mArchitectView.callJavascript(JS_GET_USER_DEFAULTS_RESULT + "(" + obj.toString() + ")"); } catch (JSONException e) { e.printStackTrace(); } break; case METHOD_SET_USER_DEFAULT: String keySet = paramJson.optString("key"); keySet = "".equals(keySet) ? null : keySet; String setValue = paramJson.optString("value"); setValue = "".equals(setValue) ? null : setValue; String taskIdSet = paramJson.optString("taskid"); taskIdSet = "".equals(taskIdSet) ? null : taskIdSet; try { JSONObject obj = PreferenceManager.getInstance(ARActivity.this).setDefault(keySet, setValue); obj.put("taskid", taskIdSet); mArchitectView.callJavascript(JS_SET_USER_DEFAULTS_RESULT + "(" + obj.toString() + ")"); } catch (JSONException e) { e.printStackTrace(); } break; case METHOD_RESET_USER_DEFAULT: String prefixReset = paramJson.optString("prefix"); prefixReset = "".equals(prefixReset) ? null : prefixReset; String resetTaskId = paramJson.optString("taskid"); resetTaskId = "".equals(resetTaskId) ? null : resetTaskId; JSONObject obj = PreferenceManager.getInstance(ARActivity.this).deleteDefaults(prefixReset); try { obj.put("taskid", resetTaskId); mArchitectView.callJavascript(JS_RESET_USER_DEFAULTS_RESULT + "(" + obj.toString() + ")"); } catch (JSONException e) { e.printStackTrace(); } break; } } }); } private void switchChannelRequested() { loadDefaultWorld(); mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); mQrPlugin.activate(); } @Override public void onQrCodeRead(String qrContent) { mQrPlugin.deactivate(); //only react to qr codes when we are in QR or scan page mode if ((mGuiHelper.mGuiState == GUI_STATE.SCAN_QR || mGuiHelper.mGuiState == GUI_STATE.SCAN) && qrContent != null) { Log.d(TAG, "qr: " + qrContent); mArchitectView.callJavascript(JS_BARCODE_RECEIVED + "('" + qrContent + "');"); } else { mQrPlugin.activate(); } } private void processQr(String qrContent) { Uri qrUri = Uri.parse(qrContent); if (QR_TARTT_SCHEME.equals(qrUri.getScheme()) && QR_CHANNEL_HOST.equals(qrUri.getHost()) && qrUri.getQueryParameter(Channel.CHANNEL_KEY) != null) { //we now might also be receiving a scanned qr/barcode out of an active world, so let's make sure to deactivate the world's AR loadDefaultWorld(); mGuiHelper.changeGuiState(GUI_STATE.LOADING); TarttRequestOptions newConfig = Tartt .getInitialConfigForChannel(qrUri.getQueryParameter(Channel.CHANNEL_KEY)); try { if (qrUri.getQueryParameter(Channel.STATE) != null) { newConfig.setState(TarttRequestOptions.State .getStateForValue(Integer.valueOf(qrUri.getQueryParameter(Channel.STATE)))); } if (qrUri.getQueryParameter(Channel.TARGET_TYPE) != null) { newConfig.setTargetType( TarttRequestOptions.TargetType.valueOf(qrUri.getQueryParameter(Channel.TARGET_TYPE))); } if (qrUri.getQueryParameter(Channel.ENV_TYPE) != null) { newConfig.setEnvType( TarttRequestOptions.EnvironmentType.valueOf(qrUri.getQueryParameter(Channel.ENV_TYPE))); } if (qrUri.getQueryParameter(Channel.TARGET_API) != null) { newConfig.setTargetApi(Integer.valueOf(qrUri.getQueryParameter(Channel.TARGET_API))); } if (qrUri.getQueryParameter(Channel.LANGUAGE) != null) { newConfig.setLanguage(qrUri.getQueryParameter(Channel.LANGUAGE)); } } catch (NumberFormatException eNFE) { Log.e(TAG, "QR-Code contained invalid value for parameter (requires a number)"); } catch (IllegalArgumentException eIAE) { Log.e(TAG, "QR-Code contained invalid value for a parameter (not in valid range)"); } mChannelManager.fetchFilesForChannelWithConfig(newConfig, this); } else { mQrPlugin.activate(); } } @Override public void worldLoadFailed(int i, String s, String s1) { Log.d(TAG, "worldLoadFailed i: " + i + " s: " + s + " s1: " + s1); } @Override public void onMultipleChannelsAvailable() { loadDefaultWorld(); mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); mQrPlugin.activate(); } @Override public void onNoChannelFound(TarttRequestOptions tarttRequestOptions) { if (!LANG_FALLBACK.equals(tarttRequestOptions.getLanguage())) { Log.d(TAG, "onNoChannelFound fall back to " + LANG_FALLBACK); //No worlds could be initialized for the default language, so we fall back to LANG_FALLBACK tarttRequestOptions.setLanguage(LANG_FALLBACK); if (tarttRequestOptions.getChannelKey() == null) { //we fallback for our initial call, so we still want onMultipleChannelsAvailable() to be invoked if multiple channels are found that we can select between mChannelManager.findChannelsWithConfig(tarttRequestOptions, this); } else { //we fallback for a call that already has a channel specified, so we simply want to get the latest data set for it mChannelManager.fetchFilesForChannelWithConfig(tarttRequestOptions, this); } } else { Log.e(TAG, "onNoChannelFound no fallback"); Toast.makeText(this, "No channels found", Toast.LENGTH_SHORT).show(); if (tarttRequestOptions.getChannelKey() == null) { //we fallback for our initial call, no point in showing the qr scanner mGuiHelper.changeGuiState(GUI_STATE.HIDE); } else { //we fallback for a call that already has a channel specified, so we let the user select a different one loadDefaultWorld(); mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); mQrPlugin.activate(); } } } @Override public void onDownloadStarted(long bytesToLoad) { Log.d(TAG, "onDownloadStarted " + bytesToLoad); mBytesToLoad = bytesToLoad; } @Override public void onDownloadProgress(long bytesLoaded) { int progressPercent = (int) ((bytesLoaded * 100) / mBytesToLoad); if (progressPercent >= 1) { //only switch to progress bar when we show some progress, otherwise it'd be barely visible if (mGuiHelper.mGuiState != GUI_STATE.INITIAL_PROGRESS && mGuiHelper.mGuiState != GUI_STATE.PROGRESS) { mGuiHelper.changeGuiState( mGuiHelper.mGuiState == GUI_STATE.INITIAL_LOADING ? GUI_STATE.INITIAL_PROGRESS : GUI_STATE.PROGRESS); } mGuiHelper.mProgressBar.setProgress(progressPercent); } } @Override public void onNewChannelFilesAvailable(String channelKey, File folder) { Log.d(TAG, "onNewChannelFilesAvailable " + folder.getPath()); //hiding the cancel button, because we can't cancel the wikitude loading mGuiHelper.changeGuiState((mGuiHelper.mGuiState == GUI_STATE.INITIAL_LOADING || mGuiHelper.mGuiState == GUI_STATE.INITIAL_PROGRESS) ? GUI_STATE.INITIAL_LOADING : GUI_STATE.LOADING_NO_CANCEL); try { if (this.mArchitectView != null) { mArchitectView.callJavascript(JS_DESTROY); Log.d(TAG, "load " + "file://" + folder.getPath() + File.separator + "index.html"); mArchitectView.load("file://" + folder.getPath() + File.separator + "index.html"); } } catch (IOException e) { Log.e(TAG, "An error occured loading the AR world"); Toast.makeText(ARActivity.this, "An error occured loading the AR world", Toast.LENGTH_SHORT).show(); } } @Override public void onError(ChannelManager.TARTT_ERROR tarttError) { Log.e(TAG, "onError " + tarttError); if (mGuiHelper.mGuiState == GUI_STATE.INITIAL_LOADING || mGuiHelper.mGuiState == GUI_STATE.INITIAL_PROGRESS) { mGuiHelper.changeGuiState(GUI_STATE.RETRY); } else { loadDefaultWorld(); mGuiHelper.changeGuiState(GUI_STATE.SCAN_QR); mQrPlugin.activate(); Toast.makeText(ARActivity.this, "An error occured", Toast.LENGTH_SHORT).show(); } } }