diff --git a/app/build.gradle b/app/build.gradle index 244ad1a..d0e98c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "com.afollestad.nocknock" minSdkVersion 21 targetSdkVersion 24 - versionCode 8 - versionName "0.1.1.0" + versionCode 9 + versionName "0.1.2.0" jackOptions { enabled true @@ -34,6 +34,7 @@ dependencies { compile 'com.afollestad.material-dialogs:core:0.8.6.2' compile 'com.afollestad.material-dialogs:commons:0.8.6.2' compile 'com.afollestad:bridge:3.2.5' - compile 'com.afollestad:inquiry:2.0.2' + compile 'com.afollestad:inquiry:3.1.0' compile 'com.android.support:design:24.1.1' + compile files('libs/rhino-1.7.7.1.jar') } \ No newline at end of file diff --git a/app/libs/rhino-1.7.7.1.jar b/app/libs/rhino-1.7.7.1.jar new file mode 100644 index 0000000..a8b9417 Binary files /dev/null and b/app/libs/rhino-1.7.7.1.jar differ diff --git a/app/src/main/java/com/afollestad/nocknock/adapter/ServerAdapter.java b/app/src/main/java/com/afollestad/nocknock/adapter/ServerAdapter.java index f4e3531..b4da56b 100644 --- a/app/src/main/java/com/afollestad/nocknock/adapter/ServerAdapter.java +++ b/app/src/main/java/com/afollestad/nocknock/adapter/ServerAdapter.java @@ -116,7 +116,7 @@ public class ServerAdapter extends RecyclerView.Adapter holder.textStatus.setText(R.string.checking_status); break; case ServerStatus.ERROR: - holder.textStatus.setText(R.string.something_wrong); + holder.textStatus.setText(model.reason); break; } diff --git a/app/src/main/java/com/afollestad/nocknock/api/ServerModel.java b/app/src/main/java/com/afollestad/nocknock/api/ServerModel.java index 6e48b59..1e3b6df 100644 --- a/app/src/main/java/com/afollestad/nocknock/api/ServerModel.java +++ b/app/src/main/java/com/afollestad/nocknock/api/ServerModel.java @@ -27,4 +27,10 @@ public class ServerModel implements Serializable { public long lastCheck; @Column public String reason; + + @Column + @ValidationMode.Enum + public int validationMode; + @Column + public String validationContent; } \ No newline at end of file diff --git a/app/src/main/java/com/afollestad/nocknock/api/ValidationMode.java b/app/src/main/java/com/afollestad/nocknock/api/ValidationMode.java new file mode 100644 index 0000000..2606635 --- /dev/null +++ b/app/src/main/java/com/afollestad/nocknock/api/ValidationMode.java @@ -0,0 +1,21 @@ +package com.afollestad.nocknock.api; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author Aidan Follestad (afollestad) + */ +public final class ValidationMode { + + public final static int STATUS_CODE = 1; + public final static int TERM_SEARCH = 2; + public final static int JAVASCRIPT = 3; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATUS_CODE, TERM_SEARCH, JAVASCRIPT}) + public @interface Enum { + } +} diff --git a/app/src/main/java/com/afollestad/nocknock/receivers/BootReceiver.java b/app/src/main/java/com/afollestad/nocknock/receivers/BootReceiver.java index a925ad9..4f5666f 100644 --- a/app/src/main/java/com/afollestad/nocknock/receivers/BootReceiver.java +++ b/app/src/main/java/com/afollestad/nocknock/receivers/BootReceiver.java @@ -17,11 +17,12 @@ public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { - Inquiry.init(context, MainActivity.DB_NAME, 1); - ServerModel[] models = Inquiry.get() + final Inquiry inq = Inquiry.newInstance(context, MainActivity.DB_NAME).build(false); + ServerModel[] models = inq .selectFrom(MainActivity.SITES_TABLE_NAME, ServerModel.class) .all(); AlarmUtil.setSiteChecks(context, models); + inq.destroyInstance(); } } } diff --git a/app/src/main/java/com/afollestad/nocknock/receivers/ConnectivityReceiver.java b/app/src/main/java/com/afollestad/nocknock/receivers/ConnectivityReceiver.java index 7851035..7ceed62 100644 --- a/app/src/main/java/com/afollestad/nocknock/receivers/ConnectivityReceiver.java +++ b/app/src/main/java/com/afollestad/nocknock/receivers/ConnectivityReceiver.java @@ -17,8 +17,9 @@ public class ConnectivityReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { final boolean hasInternet = NetworkUtil.hasInternet(context); Log.v("ConnectivityReceiver", "Connectivity state changed... has internet? " + hasInternet); - if (hasInternet) + if (hasInternet) { context.startService(new Intent(context, CheckService.class) .putExtra(CheckService.ONLY_WAITING, true)); + } } } diff --git a/app/src/main/java/com/afollestad/nocknock/services/CheckService.java b/app/src/main/java/com/afollestad/nocknock/services/CheckService.java index 34302c5..550949f 100644 --- a/app/src/main/java/com/afollestad/nocknock/services/CheckService.java +++ b/app/src/main/java/com/afollestad/nocknock/services/CheckService.java @@ -16,14 +16,17 @@ import android.widget.Toast; import com.afollestad.bridge.Bridge; import com.afollestad.bridge.BridgeException; +import com.afollestad.bridge.Response; import com.afollestad.inquiry.Inquiry; import com.afollestad.inquiry.Query; import com.afollestad.nocknock.BuildConfig; import com.afollestad.nocknock.R; import com.afollestad.nocknock.api.ServerModel; import com.afollestad.nocknock.api.ServerStatus; +import com.afollestad.nocknock.api.ValidationMode; import com.afollestad.nocknock.ui.MainActivity; import com.afollestad.nocknock.ui.ViewSiteActivity; +import com.afollestad.nocknock.util.JsUtil; import com.afollestad.nocknock.util.NetworkUtil; import java.util.Locale; @@ -31,6 +34,7 @@ import java.util.Locale; /** * @author Aidan Follestad (afollestad) */ +@SuppressWarnings("CheckResult") public class CheckService extends Service { public static String ACTION_CHECK_UPDATE = BuildConfig.APPLICATION_ID + ".CHECK_UPDATE"; @@ -89,7 +93,7 @@ public class CheckService extends Service { } private void updateStatus(ServerModel site) { - Inquiry.get() + Inquiry.get(this) .update(MainActivity.SITES_TABLE_NAME, ServerModel.class) .where("_id = ?", site.id) .values(site) @@ -151,13 +155,13 @@ public class CheckService extends Service { return START_NOT_STICKY; } - Inquiry.init(this, MainActivity.DB_NAME, 1); + Inquiry.newInstance(this, MainActivity.DB_NAME).build(); isRunning(true); Bridge.config() .defaultHeader("User-Agent", getString(R.string.app_name) + " (Android)"); new Thread(() -> { - final Query query = Inquiry.get() + final Query query = Inquiry.get(this) .selectFrom(MainActivity.SITES_TABLE_NAME, ServerModel.class); if (intent != null && intent.hasExtra(MODEL_ID)) { query.where("_id = ?", intent.getLongExtra(MODEL_ID, -1)); @@ -190,12 +194,27 @@ public class CheckService extends Service { updateStatus(site); try { - Bridge.get(site.url) + final Response response = Bridge.get(site.url) .throwIfNotSuccess() .cancellable(false) - .request(); + .request() + .response(); + site.reason = null; site.status = ServerStatus.OK; + + if (site.validationMode == ValidationMode.TERM_SEARCH) { + final String body = response.asString(); + if (body == null || !body.contains(site.validationContent)) { + site.status = ServerStatus.ERROR; + site.reason = "Term \"" + site.validationContent + "\" not found in response body."; + } + } else if (site.validationMode == ValidationMode.JAVASCRIPT) { + final String body = response.asString(); + site.reason = JsUtil.exec(site.validationContent, body); + if (site.reason != null && !site.toString().isEmpty()) + site.status = ServerStatus.ERROR; + } } catch (BridgeException e) { processError(e, site); } @@ -212,4 +231,10 @@ public class CheckService extends Service { return START_STICKY; } + + @Override + public void onDestroy() { + Inquiry.destroy(this); + super.onDestroy(); + } } diff --git a/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.java b/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.java index b4d2478..837f2a3 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.java +++ b/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.java @@ -15,6 +15,7 @@ import android.view.ViewAnimationUtils; import android.view.ViewTreeObserver; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; @@ -23,6 +24,7 @@ import android.widget.TextView; import com.afollestad.nocknock.R; import com.afollestad.nocknock.api.ServerModel; import com.afollestad.nocknock.api.ServerStatus; +import com.afollestad.nocknock.api.ValidationMode; /** * @author Aidan Follestad (afollestad) @@ -37,6 +39,7 @@ public class AddSiteActivity extends AppCompatActivity implements View.OnClickLi private EditText inputInterval; private Spinner spinnerInterval; private TextView textUrlWarning; + private Spinner responseValidationSpinner; private boolean isClosing; @@ -51,6 +54,7 @@ public class AddSiteActivity extends AppCompatActivity implements View.OnClickLi textUrlWarning = (TextView) findViewById(R.id.textUrlWarning); inputInterval = (EditText) findViewById(R.id.checkIntervalInput); spinnerInterval = (Spinner) findViewById(R.id.checkIntervalSpinner); + responseValidationSpinner = (Spinner) findViewById(R.id.responseValidationMode); toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setNavigationOnClickListener(view -> closeActivityWithReveal()); @@ -71,6 +75,7 @@ public class AddSiteActivity extends AppCompatActivity implements View.OnClickLi ArrayAdapter intervalOptionsAdapter = new ArrayAdapter<>(this, R.layout.list_item_spinner, getResources().getStringArray(R.array.interval_options)); + intervalOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown); spinnerInterval.setAdapter(intervalOptionsAdapter); inputUrl.setOnFocusChangeListener((view, hasFocus) -> { @@ -90,6 +95,38 @@ public class AddSiteActivity extends AppCompatActivity implements View.OnClickLi } }); + ArrayAdapter validationOptionsAdapter = new ArrayAdapter<>(this, R.layout.list_item_spinner, + getResources().getStringArray(R.array.response_validation_options)); + validationOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown); + responseValidationSpinner.setAdapter(validationOptionsAdapter); + responseValidationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + final View searchTerm = findViewById(R.id.responseValidationSearchTerm); + final View javascript = findViewById(R.id.responseValidationScript); + final TextView modeDesc = (TextView) findViewById(R.id.validationModeDescription); + + searchTerm.setVisibility(i == 1 ? View.VISIBLE : View.GONE); + javascript.setVisibility(i == 2 ? View.VISIBLE : View.GONE); + + switch (i) { + case 0: + modeDesc.setText(R.string.validation_mode_status_desc); + break; + case 1: + modeDesc.setText(R.string.validation_mode_term_desc); + break; + case 2: + modeDesc.setText(R.string.validation_mode_javascript_desc); + break; + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + } + }); + findViewById(R.id.doneBtn).setOnClickListener(this); } @@ -192,6 +229,21 @@ public class AddSiteActivity extends AppCompatActivity implements View.OnClickLi model.lastCheck = System.currentTimeMillis() - model.checkInterval; + switch (responseValidationSpinner.getSelectedItemPosition()) { + case 0: + model.validationMode = ValidationMode.STATUS_CODE; + model.validationContent = null; + break; + case 1: + model.validationMode = ValidationMode.TERM_SEARCH; + model.validationContent = ((EditText) findViewById(R.id.responseValidationSearchTerm)).getText().toString().trim(); + break; + case 2: + model.validationMode = ValidationMode.JAVASCRIPT; + model.validationContent = ((EditText) findViewById(R.id.responseValidationScriptInput)).getText().toString().trim().replace("\n", " "); + break; + } + setResult(RESULT_OK, new Intent() .putExtra("model", model)); finish(); diff --git a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.java b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.java index c2e660d..88f8ba8 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.java +++ b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.java @@ -3,6 +3,7 @@ package com.afollestad.nocknock.ui; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.content.BroadcastReceiver; import android.content.Context; @@ -33,6 +34,7 @@ import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.nocknock.R; import com.afollestad.nocknock.adapter.ServerAdapter; import com.afollestad.nocknock.api.ServerModel; +import com.afollestad.nocknock.api.ValidationMode; import com.afollestad.nocknock.dialogs.AboutDialog; import com.afollestad.nocknock.services.CheckService; import com.afollestad.nocknock.util.AlarmUtil; @@ -44,7 +46,8 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou private final static int ADD_SITE_RQ = 6969; private final static int VIEW_SITE_RQ = 6923; public final static String DB_NAME = "nock_nock"; - public final static String SITES_TABLE_NAME = "sites"; + public final static String SITES_TABLE_NAME_OLD = "sites"; + public final static String SITES_TABLE_NAME = "site_models"; private FloatingActionButton mFab; private RecyclerView mList; @@ -72,6 +75,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou } }; + @SuppressLint("CommitPrefEdits") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -94,9 +98,33 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou mFab = (FloatingActionButton) findViewById(R.id.fab); mFab.setOnClickListener(this); - Inquiry.init(this, DB_NAME, 1); + Inquiry.newInstance(this, DB_NAME).build(); Bridge.config() .defaultHeader("User-Agent", getString(R.string.app_name) + " (Android)"); + + final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + if (!sp.getBoolean("migrated_db", false)) { + final Inquiry mdb = Inquiry.newInstance(this, DB_NAME) + .instanceName("migrate_db") + .build(false); + final ServerModel[] models = Inquiry.get(this) + .selectFrom(SITES_TABLE_NAME_OLD, ServerModel.class) + .projection("_id", "name", "url", "status", "checkInterval", "lastCheck", "reason") + .all(); + if (models != null) { + Log.d("SiteMigration", "Migrating " + models.length + " sites to the new table."); + for (ServerModel model : models) { + model.validationMode = ValidationMode.STATUS_CODE; + model.validationContent = null; + } + //noinspection CheckResult + mdb.insertInto(SITES_TABLE_NAME, ServerModel.class) + .values(models) + .run(); + mdb.dropTable(SITES_TABLE_NAME_OLD); + } + sp.edit().putBoolean("migrated_db", true).commit(); + } } private void showRefreshTutorial() { @@ -141,6 +169,10 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou super.onPause(); CheckService.isAppOpen(this, false); + if (isFinishing()) { + Inquiry.destroy(this); + } + NotificationManagerCompat.from(this).cancel(CheckService.NOTI_ID); try { unregisterReceiver(mReceiver); @@ -152,7 +184,7 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou private void refreshModels() { mAdapter.clear(); mEmptyText.setVisibility(View.VISIBLE); - Inquiry.get() + Inquiry.get(this) .selectFrom(SITES_TABLE_NAME, ServerModel.class) .all(this::setModels); } @@ -225,28 +257,21 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou if (requestCode == ADD_SITE_RQ) { mAdapter.add(model); mEmptyText.setVisibility(View.GONE); - Inquiry.get().insertInto(SITES_TABLE_NAME, ServerModel.class) + Inquiry.get(this).insertInto(SITES_TABLE_NAME, ServerModel.class) .values(model) .run(inserted -> { AlarmUtil.setSiteChecks(MainActivity.this, model); checkSite(MainActivity.this, model); }); } else if (requestCode == VIEW_SITE_RQ) { - Inquiry.get() - .update(MainActivity.SITES_TABLE_NAME, ServerModel.class) - .where("_id = ?", model.id) - .values(model) - .run(changed -> { - mAdapter.update(model); - AlarmUtil.setSiteChecks(MainActivity.this, model); - checkSite(MainActivity.this, model); - }); + mAdapter.update(model); + AlarmUtil.setSiteChecks(MainActivity.this, model); + checkSite(MainActivity.this, model); } } } public static void removeSite(final Context context, final ServerModel model, final Runnable onRemoved) { - Inquiry.init(context, DB_NAME, 1); new MaterialDialog.Builder(context) .title(R.string.remove_site) .content(Html.fromHtml(context.getString(R.string.remove_site_prompt, model.name))) @@ -256,10 +281,15 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou AlarmUtil.cancelSiteChecks(context, model); final NotificationManagerCompat nm = NotificationManagerCompat.from(context); nm.cancel(model.url, CheckService.NOTI_ID); - Inquiry.get() - .deleteFrom(SITES_TABLE_NAME, ServerModel.class) + //noinspection CheckResult + final Inquiry rinq = Inquiry.newInstance(context, DB_NAME) + .instanceName("remove_site") + .build(false); + //noinspection CheckResult + rinq.deleteFrom(SITES_TABLE_NAME, ServerModel.class) .where("_id = ?", model.id) .run(); + rinq.destroyInstance(); if (onRemoved != null) onRemoved.run(); }).show(); diff --git a/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.java b/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.java index bc7e6ac..2ce4c48 100644 --- a/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.java +++ b/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.java @@ -14,15 +14,18 @@ import android.util.Log; import android.util.Patterns; import android.view.MenuItem; import android.view.View; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import com.afollestad.bridge.Bridge; +import com.afollestad.inquiry.Inquiry; import com.afollestad.nocknock.R; import com.afollestad.nocknock.api.ServerModel; import com.afollestad.nocknock.api.ServerStatus; +import com.afollestad.nocknock.api.ValidationMode; import com.afollestad.nocknock.services.CheckService; import com.afollestad.nocknock.util.TimeUtil; import com.afollestad.nocknock.views.StatusImageView; @@ -44,6 +47,7 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL private TextView textLastCheckResult; private TextView textNextCheck; private TextView textUrlWarning; + private Spinner responseValidationSpinner; private ServerModel mModel; @@ -77,9 +81,11 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL checkIntervalSpinner = (Spinner) findViewById(R.id.checkIntervalSpinner); textLastCheckResult = (TextView) findViewById(R.id.textLastCheckResult); textNextCheck = (TextView) findViewById(R.id.textNextCheck); + responseValidationSpinner = (Spinner) findViewById(R.id.responseValidationMode); ArrayAdapter intervalOptionsAdapter = new ArrayAdapter<>(this, R.layout.list_item_spinner, getResources().getStringArray(R.array.interval_options)); + intervalOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown); checkIntervalSpinner.setAdapter(intervalOptionsAdapter); inputUrl.setOnFocusChangeListener((view, hasFocus) -> { @@ -99,6 +105,38 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL } }); + ArrayAdapter validationOptionsAdapter = new ArrayAdapter<>(this, R.layout.list_item_spinner, + getResources().getStringArray(R.array.response_validation_options)); + validationOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown); + responseValidationSpinner.setAdapter(validationOptionsAdapter); + responseValidationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + final View searchTerm = findViewById(R.id.responseValidationSearchTerm); + final View javascript = findViewById(R.id.responseValidationScript); + final TextView modeDesc = (TextView) findViewById(R.id.validationModeDescription); + + searchTerm.setVisibility(i == 1 ? View.VISIBLE : View.GONE); + javascript.setVisibility(i == 2 ? View.VISIBLE : View.GONE); + + switch (i) { + case 0: + modeDesc.setText(R.string.validation_mode_status_desc); + break; + case 1: + modeDesc.setText(R.string.validation_mode_term_desc); + break; + case 2: + modeDesc.setText(R.string.validation_mode_javascript_desc); + break; + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + } + }); + mModel = (ServerModel) getIntent().getSerializableExtra("model"); update(); @@ -115,7 +153,7 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL } } - @SuppressLint("SetTextI18n") + @SuppressLint({"SetTextI18n", "SwitchIntDef"}) private void update() { final SimpleDateFormat df = new SimpleDateFormat("MMMM dd, hh:mm:ss a", Locale.getDefault()); @@ -169,6 +207,16 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL } } + responseValidationSpinner.setSelection(mModel.validationMode - 1); + switch (mModel.validationMode) { + case ValidationMode.TERM_SEARCH: + ((TextView) findViewById(R.id.responseValidationSearchTerm)).setText(mModel.validationContent); + break; + case ValidationMode.JAVASCRIPT: + ((TextView) findViewById(R.id.responseValidationScriptInput)).setText(mModel.validationContent); + break; + } + findViewById(R.id.doneBtn).setOnClickListener(this); } @@ -195,26 +243,24 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL } } - // Save button - @Override - public void onClick(View view) { + private void performSave(boolean withValidation) { mModel.name = inputName.getText().toString().trim(); mModel.url = inputUrl.getText().toString().trim(); mModel.status = ServerStatus.WAITING; - if (mModel.name.isEmpty()) { + if (withValidation && mModel.name.isEmpty()) { inputName.setError(getString(R.string.please_enter_name)); return; } else { inputName.setError(null); } - if (mModel.url.isEmpty()) { + if (withValidation && mModel.url.isEmpty()) { inputUrl.setError(getString(R.string.please_enter_url)); return; } else { inputUrl.setError(null); - if (!Patterns.WEB_URL.matcher(mModel.url).find()) { + if (withValidation && !Patterns.WEB_URL.matcher(mModel.url).find()) { inputUrl.setError(getString(R.string.please_enter_valid_url)); return; } else { @@ -245,6 +291,35 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL mModel.lastCheck = System.currentTimeMillis() - mModel.checkInterval; + switch (responseValidationSpinner.getSelectedItemPosition()) { + case 0: + mModel.validationMode = ValidationMode.STATUS_CODE; + mModel.validationContent = null; + break; + case 1: + mModel.validationMode = ValidationMode.TERM_SEARCH; + mModel.validationContent = ((EditText) findViewById(R.id.responseValidationSearchTerm)).getText().toString().trim(); + break; + case 2: + mModel.validationMode = ValidationMode.JAVASCRIPT; + mModel.validationContent = ((EditText) findViewById(R.id.responseValidationScriptInput)).getText().toString().trim().replace("\n", " "); + break; + } + + final Inquiry inq = Inquiry.newInstance(this, MainActivity.DB_NAME) + .build(false); + //noinspection CheckResult + inq.update(MainActivity.SITES_TABLE_NAME, ServerModel.class) + .where("_id = ?", mModel.id) + .values(mModel) + .run(); + inq.destroyInstance(); + } + + // Save button + @Override + public void onClick(View view) { + performSave(true); setResult(RESULT_OK, new Intent().putExtra("model", mModel)); finish(); } @@ -253,10 +328,11 @@ public class ViewSiteActivity extends AppCompatActivity implements View.OnClickL public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.refresh: + performSave(false); MainActivity.checkSite(this, mModel); return true; case R.id.remove: - MainActivity.removeSite(this, mModel, () -> finish()); + MainActivity.removeSite(this, mModel, this::finish); return true; } return false; diff --git a/app/src/main/java/com/afollestad/nocknock/util/JsUtil.java b/app/src/main/java/com/afollestad/nocknock/util/JsUtil.java new file mode 100644 index 0000000..160927b --- /dev/null +++ b/app/src/main/java/com/afollestad/nocknock/util/JsUtil.java @@ -0,0 +1,72 @@ +package com.afollestad.nocknock.util; + +import android.util.Log; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.EvaluatorException; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; + +/** + * @author Aidan Follestad (afollestad) + */ +public class JsUtil { + + public static String exec(String code, String response) { + try { + final String func = String.format( + "function validate(response) { " + + "try { " + + "%s " + + "} catch(e) { " + + "return e; " + + "} " + + "}", code.replace("\n", " ")); + + // Every Rhino VM begins with the enter() + // This Context is not Android's Context + Context rhino = Context.enter(); + + // Turn off optimization to make Rhino Android compatible + rhino.setOptimizationLevel(-1); + try { + Scriptable scope = rhino.initStandardObjects(); + + // Note the forth argument is 1, which means the JavaScript source has + // been compressed to only one line using something like YUI + rhino.evaluateString(scope, func, "JavaScript", 1, null); + + // Get the functionName defined in JavaScriptCode + Function jsFunction = (Function) scope.get("validate", scope); + + // Call the function with params + Object jsResult = jsFunction.call(rhino, scope, scope, new Object[]{response}); + + // Parse the jsResult object to a String + String result = Context.toString(jsResult); + + boolean success = result != null && result.equals("true"); + String message = "The script returned a value other than true!"; + if (!success && result != null && !result.equals("false")) { + if (result.equals("undefined")) { + message = "The script did not return or throw anything!"; + } else { + message = result; + } + } + + Log.d("JsUtil", "Evaluated to " + message + " (" + success + "): " + code); + return !success ? message : null; + } finally { + Context.exit(); + } + } catch (EvaluatorException e) { + return e.getMessage(); + + + } + } + + private JsUtil() { + } +} diff --git a/app/src/main/res/layout/activity_addsite.xml b/app/src/main/res/layout/activity_addsite.xml index 0234e47..2322dfb 100644 --- a/app/src/main/res/layout/activity_addsite.xml +++ b/app/src/main/res/layout/activity_addsite.xml @@ -26,8 +26,7 @@ android:orientation="vertical" android:paddingBottom="@dimen/content_inset" android:paddingLeft="@dimen/content_inset" - android:paddingRight="@dimen/content_inset" - android:paddingTop="@dimen/content_inset_less"> + android:paddingRight="@dimen/content_inset"> + + + + + + + + + + + + + + + + + + + + + + + + + +