diff --git a/app/src/main/java/com/codigoparallevar/minicards/CardActivity.java b/app/src/main/java/com/codigoparallevar/minicards/CardActivity.java index ac142e4..4249320 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/CardActivity.java +++ b/app/src/main/java/com/codigoparallevar/minicards/CardActivity.java @@ -2,15 +2,27 @@ package com.codigoparallevar.minicards; import android.content.Context; import android.content.Intent; + +import com.codigoparallevar.minicards.types.functional.Consumer; +import com.codigoparallevar.minicards.types.functional.Producer; +import com.codigoparallevar.minicards.types.functional.Tuple2; +import com.codigoparallevar.minicards.ui_helpers.GetAsync; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.programaker.api.ProgramakerApi; +import com.programaker.api.data.ProgramakerCustomBlock; +import com.programaker.api.data.api_results.ProgramakerGetCustomBlocksResult; + import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; + +import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import java.io.IOException; +import java.util.List; public class CardActivity extends AppCompatActivity { @@ -19,7 +31,6 @@ public class CardActivity extends AppCompatActivity { public static final String VISUALIZATION_MODE_KEY = "VISUALIZATION_MODE"; public static final String DEVELOPER_VISUALIZATION_MODE = "DEVELOPER_VISUALIZATION_MODE"; - CanvasView canvasView; com.getbase.floatingactionbutton.AddFloatingActionButton AddPartButton; com.getbase.floatingactionbutton.FloatingActionButton SetDevModeButton; @@ -34,11 +45,39 @@ public class CardActivity extends AppCompatActivity { boolean devMode = false; FloatingActionButton removePartFab; private PartsHolder partsHolder; + private ProgramakerApi ProgramakerApi; + private ConfigManager Config; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + this.Config = new ConfigManager(this); + this.ProgramakerApi = new ProgramakerApi(); + String token = this.Config.getToken(); + if (token != null) { + this.ProgramakerApi.setToken(token); + } + + new GetAsync().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, + new Tuple2<>(new Producer() { + @Override + public ProgramakerGetCustomBlocksResult get() { + return CardActivity.this.ProgramakerApi.fetchCustomBlocks(); + } + }, new Consumer() { + @Override + public void apply(ProgramakerGetCustomBlocksResult result) { + if (result == null) { + Log.e("CARDActivity", "error retrieving custom blocks"); + } + else { + Log.d("CARDActivity", "custom blocks: " + result.toString()); + } + } + })); + + // Hide action bar ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/com/codigoparallevar/minicards/ConfigManager.java b/app/src/main/java/com/codigoparallevar/minicards/ConfigManager.java new file mode 100644 index 0000000..c30d300 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/ConfigManager.java @@ -0,0 +1,35 @@ +package com.codigoparallevar.minicards; + +import android.content.Context; +import android.content.SharedPreferences; +import android.view.View; + +import static android.content.Context.MODE_PRIVATE; + +public class ConfigManager { + private final Context context; + private static final String TOKEN_KEY = "PROGRAMAKER_API_TOKEN"; + private static final String PREFERENCES_NAME = "MINICARDS_PREFERENCES"; + + public ConfigManager(Context ctx) { + this.context = ctx; + } + + public void setToken(String token) { + SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE); + SharedPreferences.Editor edit = preferences.edit(); + + edit.putString(TOKEN_KEY, token); + edit.commit(); + } + + public String getToken() { + SharedPreferences preferences = this.context.getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE); + if (!preferences.contains(TOKEN_KEY)) { + return null; + } + else { + return preferences.getString(TOKEN_KEY, null); + } + } +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java b/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java index 722ae9f..de62056 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java +++ b/app/src/main/java/com/codigoparallevar/minicards/DeckPreviewActivity.java @@ -2,11 +2,10 @@ package com.codigoparallevar.minicards; import android.app.Dialog; import android.content.DialogInterface; -import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; -import com.codigoparallevar.minicards.types.functional.Function; +import com.codigoparallevar.minicards.types.functional.Consumer; import com.codigoparallevar.minicards.types.functional.Tuple2; import com.codigoparallevar.minicards.types.functional.Tuple3; import com.google.android.material.floatingactionbutton.FloatingActionButton; @@ -32,13 +31,11 @@ import java.util.Vector; import com.programaker.api.ProgramakerApi; public class DeckPreviewActivity extends ReloadableAppCompatActivity { - private static final String TOKEN_KEY = "PROGRAMAKER_API_TOKEN"; - private static final String PREFERENCES_NAME = "MINICARDS_PREFERENCES"; - public static final String INTENT = "com.codigoparallevar.minicards.DECK"; private ListView listView; private CardPreviewArrayAdapter cardArrayAdapter; - private ProgramakerApi ProgramakerApi = new ProgramakerApi(); + private ProgramakerApi ProgramakerApi; + private ConfigManager Config; protected void openLoginDialog(View view) { AlertDialog.Builder builder = new AlertDialog.Builder(this); @@ -95,12 +92,13 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { new Tuple3<>( loginUsernameText.getText().toString(), loginPasswordText.getText().toString(), - new Function() { + new Consumer() { public void apply(String token) { if (token == null) { messageLabel.setText(R.string.invalid_user_pass); } else { - DeckPreviewActivity.this.setToken(token); + DeckPreviewActivity.this.Config.setToken(token); + DeckPreviewActivity.this.ProgramakerApi.setToken(token); final Button loginToProgramakerButton = findViewById(R.id.login_in_programaker_button); loginToProgramakerButton.setVisibility(View.GONE); @@ -116,30 +114,10 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { }); } - private void setToken(String token) { - SharedPreferences preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE); - SharedPreferences.Editor edit = preferences.edit(); - - edit.putString(TOKEN_KEY, token); - edit.commit(); - - this.ProgramakerApi.setToken(token); - } - - private String getToken() { - SharedPreferences preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE); - if (!preferences.contains(TOKEN_KEY)) { - return null; - } - else { - return preferences.getString(TOKEN_KEY, null); - } - } - - static class CheckLogin extends AsyncTask>, - Void, Tuple2>>{ + static class CheckLogin extends AsyncTask>, + Void, Tuple2>>{ @Override - protected Tuple2> doInBackground(Tuple3>... tuples) { + protected Tuple2> doInBackground(Tuple3>... tuples) { ProgramakerApi api = new ProgramakerApi(); boolean logged = false; String token = null; @@ -149,11 +127,11 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { catch (Exception e) { Log.e("Login to PrograMaker", e.toString()); } - return new Tuple2>(token, tuples[0]._z); + return new Tuple2>(token, tuples[0]._z); } @Override - protected void onPostExecute(Tuple2> result) { + protected void onPostExecute(Tuple2> result) { result.item2.apply(result.item1); } } @@ -175,6 +153,10 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + this.Config = new ConfigManager(this); + this.ProgramakerApi = new ProgramakerApi(); + setContentView(R.layout.activity_deck_preview); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -196,17 +178,16 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity { }); listView = findViewById(R.id.card_deck_list); - - String token = getToken(); + String token = this.Config.getToken(); if (token == null) { loginButton.setVisibility(View.VISIBLE); } else { - loginButton.setVisibility(View.GONE); this.ProgramakerApi.setToken(token); - // Double check that is not needed + loginButton.setVisibility(View.GONE); + // Double check that is not needed, token might have been deleted new CheckNeededLoginButton().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, - new Tuple2<>(this.ProgramakerApi, loginButton)); + new Tuple2<>(this.ProgramakerApi, loginButton)); } } diff --git a/app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Consumer.java similarity index 73% rename from app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java rename to app/src/main/java/com/codigoparallevar/minicards/types/functional/Consumer.java index d9ab706..e321ace 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java +++ b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Consumer.java @@ -1,5 +1,5 @@ package com.codigoparallevar.minicards.types.functional; -public abstract class Function { +public abstract class Consumer { public abstract void apply(T param); } diff --git a/app/src/main/java/com/codigoparallevar/minicards/types/functional/Producer.java b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Producer.java new file mode 100644 index 0000000..c61a648 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Producer.java @@ -0,0 +1,5 @@ +package com.codigoparallevar.minicards.types.functional; + +public abstract class Producer { + public abstract T get(); +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/ui_helpers/GetAsync.java b/app/src/main/java/com/codigoparallevar/minicards/ui_helpers/GetAsync.java new file mode 100644 index 0000000..d38688b --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/ui_helpers/GetAsync.java @@ -0,0 +1,29 @@ +package com.codigoparallevar.minicards.ui_helpers; + +import android.os.AsyncTask; +import android.util.Log; + +import com.codigoparallevar.minicards.types.functional.Consumer; +import com.codigoparallevar.minicards.types.functional.Producer; +import com.codigoparallevar.minicards.types.functional.Tuple2; + +public class GetAsync extends AsyncTask, Consumer>, + Void, + Tuple2>> { + + @Override + protected Tuple2> doInBackground(Tuple2, Consumer>... data) { + try { + return new Tuple2<>(data[0].item1.get(), data[0].item2); + } + catch (Exception ex) { + Log.e("GetAsync", ex.toString()); + return new Tuple2<>(null, data[0].item2); + } + } + + @Override + protected void onPostExecute(Tuple2> result) { + result.item2.apply(result.item1); + } +} diff --git a/app/src/main/java/com/programaker/api/ProgramakerApi.kt b/app/src/main/java/com/programaker/api/ProgramakerApi.kt index f1b3f88..3bb4a90 100644 --- a/app/src/main/java/com/programaker/api/ProgramakerApi.kt +++ b/app/src/main/java/com/programaker/api/ProgramakerApi.kt @@ -3,6 +3,15 @@ package com.programaker.api import android.os.Build import android.util.Log import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.programaker.api.data.ProgramakerCustomBlock +import com.programaker.api.data.api_results.ProgramakerCheckResult +import com.programaker.api.data.api_results.ProgramakerGetCustomBlocksResult +import com.programaker.api.data.api_results.ProgramakerGetCustomBlocksResultTypeAdapter +import com.programaker.api.data.api_results.ProgramakerLoginResult +import com.programaker.api.exceptions.ProgramakerLoginRequiredException +import com.programaker.api.exceptions.ProgramakerProtocolException +import com.programaker.api.exceptions.TokenNotFoundException import org.json.JSONObject import java.io.DataOutputStream import java.io.FileNotFoundException @@ -16,6 +25,9 @@ import java.net.URLConnection class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api") { private val LogTag: String = "ProgramakerApi" + + private var userId: String? = null + private var userName: String? = null var token: String? = null // API @@ -41,9 +53,16 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api") ex.logError(LogTag) return false } + + if (result.success) { + this.userId = result.user_id; + this.userName = result.username; + } + return result.success } + fun login(user: String, password: String): String { val conn = URL(getLoginUrl()).openConnection() as HttpURLConnection conn.setRequestProperty("Content-Type", "application/json") @@ -65,6 +84,31 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api") return result.token } + fun fetchCustomBlocks(): ProgramakerGetCustomBlocksResult { + val conn = URL(getCustomBlocksUrl()).openConnection() as HttpURLConnection + + addAuthHeader(conn) + + val result: ProgramakerGetCustomBlocksResult + + try { + val builder = GsonBuilder() + builder.registerTypeAdapter(ProgramakerGetCustomBlocksResult::class.java, ProgramakerGetCustomBlocksResultTypeAdapter()) + + val gson = builder.create() + val reader = InputStreamReader(conn.inputStream) + + result = gson.fromJson(reader, ProgramakerGetCustomBlocksResult::class.java) + } catch(ex: JsonParseException) { + ex.logError(LogTag) + throw ProgramakerProtocolException() + } catch (ex: FileNotFoundException) { + ex.logError(LogTag) + throw ProgramakerProtocolException() + } + return result + } + // Initialization init { // Disable connection reuse if necessary @@ -83,6 +127,21 @@ class ProgramakerApi(private val ApiRoot: String="https://programaker.com/api") return "$ApiRoot/v0/sessions/login" } + private fun getCustomBlocksUrl(): String { + if (userName == null) { + if (token == null) { + throw ProgramakerLoginRequiredException() + } + + this.check(); + if (userName == null) { + throw ProgramakerProtocolException() + } + } + + return "$ApiRoot/v0/users/$userName/custom-blocks/" + } + private fun addAuthHeader(conn: URLConnection) { if (token != null) { conn.setRequestProperty("Authorization", token) diff --git a/app/src/main/java/com/programaker/api/ProgramakerCheckResult.kt b/app/src/main/java/com/programaker/api/ProgramakerCheckResult.kt deleted file mode 100644 index 9f8596b..0000000 --- a/app/src/main/java/com/programaker/api/ProgramakerCheckResult.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.programaker.api - -class ProgramakerCheckResult(val success: Boolean) { - -} diff --git a/app/src/main/java/com/programaker/api/TokenNotFoundException.kt b/app/src/main/java/com/programaker/api/TokenNotFoundException.kt deleted file mode 100644 index 3d383ca..0000000 --- a/app/src/main/java/com/programaker/api/TokenNotFoundException.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.programaker.api - -class TokenNotFoundException : ProgramakerConfigurationException() { -} diff --git a/app/src/main/java/com/programaker/api/data/ProgramakerCustomBlock.kt b/app/src/main/java/com/programaker/api/data/ProgramakerCustomBlock.kt new file mode 100644 index 0000000..8ca16df --- /dev/null +++ b/app/src/main/java/com/programaker/api/data/ProgramakerCustomBlock.kt @@ -0,0 +1,14 @@ +package com.programaker.api.data + +class ProgramakerCustomBlock( + val id: String, + val service_port_id: String, + val block_id: String, + val block_type: String, + val block_result_type: String, + val function_name: String, + val message: String +// val arguments: BlockArgument[], +// val save_to: undefined | string, +) { +} \ No newline at end of file diff --git a/app/src/main/java/com/programaker/api/data/api_results/ProgramakerBridgeCustomBlockResult.kt b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerBridgeCustomBlockResult.kt new file mode 100644 index 0000000..75ba9fb --- /dev/null +++ b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerBridgeCustomBlockResult.kt @@ -0,0 +1,6 @@ +package com.programaker.api.data.api_results + +import com.programaker.api.data.ProgramakerCustomBlock + +class ProgramakerBridgeCustomBlockResult(val bridge_id: String, val blocks: List) { +} diff --git a/app/src/main/java/com/programaker/api/data/api_results/ProgramakerCheckResult.kt b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerCheckResult.kt new file mode 100644 index 0000000..039c983 --- /dev/null +++ b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerCheckResult.kt @@ -0,0 +1,4 @@ +package com.programaker.api.data.api_results + +class ProgramakerCheckResult(val success: Boolean, val user_id: String, val username: String) { +} diff --git a/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResult.kt b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResult.kt new file mode 100644 index 0000000..aa7dbce --- /dev/null +++ b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResult.kt @@ -0,0 +1,4 @@ +package com.programaker.api.data.api_results + +class ProgramakerGetCustomBlocksResult(val result: List) { +} diff --git a/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResultTypeAdapter.kt b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResultTypeAdapter.kt new file mode 100644 index 0000000..c287f23 --- /dev/null +++ b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerGetCustomBlocksResultTypeAdapter.kt @@ -0,0 +1,34 @@ +package com.programaker.api.data.api_results + +import com.google.gson.* +import com.programaker.api.data.ProgramakerCustomBlock +import java.lang.reflect.Type +import java.util.* + +class ProgramakerGetCustomBlocksResultTypeAdapter : JsonSerializer, JsonDeserializer { + var gson = Gson() + override fun serialize(customBlock: ProgramakerGetCustomBlocksResult?, typeOfT: Type, context: JsonSerializationContext): JsonElement { + throw NotImplementedError() + } + + @Throws(JsonParseException::class) + override fun deserialize(element: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ProgramakerGetCustomBlocksResult { + val json = element.asJsonObject + val bridges: MutableList = LinkedList() + + for ((bridgeId, value1) in json.entrySet()) { + + val blocks: MutableList = LinkedList() + + for (value in value1.asJsonArray) { + val block = gson.fromJson(value, ProgramakerCustomBlock::class.java) + blocks.add(block) + } + + val bridge = ProgramakerBridgeCustomBlockResult(bridgeId, blocks) + bridges.add(bridge) + } + + return ProgramakerGetCustomBlocksResult(bridges) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/programaker/api/ProgramakerLoginResult.kt b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerLoginResult.kt similarity index 68% rename from app/src/main/java/com/programaker/api/ProgramakerLoginResult.kt rename to app/src/main/java/com/programaker/api/data/api_results/ProgramakerLoginResult.kt index e60b218..69c1eb6 100644 --- a/app/src/main/java/com/programaker/api/ProgramakerLoginResult.kt +++ b/app/src/main/java/com/programaker/api/data/api_results/ProgramakerLoginResult.kt @@ -1,4 +1,4 @@ -package com.programaker.api +package com.programaker.api.data.api_results class ProgramakerLoginResult(val user_id: String, val token: String, val success: Boolean) { diff --git a/app/src/main/java/com/programaker/api/ProgramakerConfigurationException.kt b/app/src/main/java/com/programaker/api/exceptions/ProgramakerConfigurationException.kt similarity index 62% rename from app/src/main/java/com/programaker/api/ProgramakerConfigurationException.kt rename to app/src/main/java/com/programaker/api/exceptions/ProgramakerConfigurationException.kt index 159a3c7..de7dd73 100644 --- a/app/src/main/java/com/programaker/api/ProgramakerConfigurationException.kt +++ b/app/src/main/java/com/programaker/api/exceptions/ProgramakerConfigurationException.kt @@ -1,4 +1,4 @@ -package com.programaker.api +package com.programaker.api.exceptions open class ProgramakerConfigurationException : Exception() { diff --git a/app/src/main/java/com/programaker/api/exceptions/ProgramakerLoginRequiredException.kt b/app/src/main/java/com/programaker/api/exceptions/ProgramakerLoginRequiredException.kt new file mode 100644 index 0000000..d7005db --- /dev/null +++ b/app/src/main/java/com/programaker/api/exceptions/ProgramakerLoginRequiredException.kt @@ -0,0 +1,5 @@ +package com.programaker.api.exceptions + +class ProgramakerLoginRequiredException : Throwable() { + +} diff --git a/app/src/main/java/com/programaker/api/ProgramakerNetworkException.kt b/app/src/main/java/com/programaker/api/exceptions/ProgramakerNetworkException.kt similarity index 58% rename from app/src/main/java/com/programaker/api/ProgramakerNetworkException.kt rename to app/src/main/java/com/programaker/api/exceptions/ProgramakerNetworkException.kt index a2c3ee1..119fc04 100644 --- a/app/src/main/java/com/programaker/api/ProgramakerNetworkException.kt +++ b/app/src/main/java/com/programaker/api/exceptions/ProgramakerNetworkException.kt @@ -1,4 +1,4 @@ -package com.programaker.api +package com.programaker.api.exceptions class ProgramakerNetworkException : Throwable() { diff --git a/app/src/main/java/com/programaker/api/exceptions/ProgramakerProtocolException.kt b/app/src/main/java/com/programaker/api/exceptions/ProgramakerProtocolException.kt new file mode 100644 index 0000000..de1a765 --- /dev/null +++ b/app/src/main/java/com/programaker/api/exceptions/ProgramakerProtocolException.kt @@ -0,0 +1,5 @@ +package com.programaker.api.exceptions + +class ProgramakerProtocolException : Throwable() { + +} diff --git a/app/src/main/java/com/programaker/api/exceptions/TokenNotFoundException.kt b/app/src/main/java/com/programaker/api/exceptions/TokenNotFoundException.kt new file mode 100644 index 0000000..9d85a30 --- /dev/null +++ b/app/src/main/java/com/programaker/api/exceptions/TokenNotFoundException.kt @@ -0,0 +1,6 @@ +package com.programaker.api.exceptions + +import com.programaker.api.exceptions.ProgramakerConfigurationException + +class TokenNotFoundException : ProgramakerConfigurationException() { +}