The view site screen works. Added more custom graphics, not permanent yet.

This commit is contained in:
Aidan Follestad 2016-07-30 17:14:38 -05:00
parent 265f34259a
commit b763d113f1
27 changed files with 639 additions and 89 deletions

View file

@ -14,7 +14,9 @@
android:theme="@style/AppTheme"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity android:name=".ui.MainActivity">
<activity
android:name=".ui.MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -25,7 +27,16 @@
<activity
android:name=".ui.AddSiteActivity"
android:label="@string/add_site"
android:theme="@style/AppTheme.Transparent" />
android:launchMode="singleTop"
android:theme="@style/AppTheme.Transparent"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".ui.ViewSiteActivity"
android:label="@string/view_site"
android:launchMode="singleTop"
android:theme="@style/AppTheme.Ink"
android:windowSoftInputMode="stateHidden" />
<service
android:name=".services.CheckService"

View file

@ -115,10 +115,14 @@ public class ServerAdapter extends RecyclerView.Adapter<ServerAdapter.ServerVH>
break;
}
final long now = System.currentTimeMillis();
final long nextCheck = model.lastCheck + model.checkInterval;
final long difference = nextCheck - now;
holder.textInterval.setText(TimeUtil.str(difference));
if (model.checkInterval <= 0) {
holder.textInterval.setText("");
} else {
final long now = System.currentTimeMillis();
final long nextCheck = model.lastCheck + model.checkInterval;
final long difference = nextCheck - now;
holder.textInterval.setText(TimeUtil.str(difference));
}
}
@Override

View file

@ -1,11 +1,16 @@
package com.afollestad.nocknock.services;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import android.widget.Toast;
@ -18,6 +23,7 @@ import com.afollestad.nocknock.R;
import com.afollestad.nocknock.api.ServerModel;
import com.afollestad.nocknock.api.ServerStatus;
import com.afollestad.nocknock.ui.MainActivity;
import com.afollestad.nocknock.ui.ViewSiteActivity;
import java.util.Locale;
@ -29,6 +35,7 @@ public class CheckService extends Service {
public static String ACTION_CHECK_UPDATE = BuildConfig.APPLICATION_ID + ".CHECK_UPDATE";
public static String ACTION_RUNNING = BuildConfig.APPLICATION_ID + ".CHECK_RUNNING";
public static String MODEL_ID = "model_id";
public static int NOTI_ID = 3456;
private static void LOG(String msg, Object... format) {
if (format != null)
@ -43,6 +50,9 @@ public class CheckService extends Service {
}
private void processError(BridgeException e, ServerModel site) {
site.status = ServerStatus.OK;
site.reason = null;
switch (e.reason()) {
case BridgeException.REASON_REQUEST_CANCELLED:
// Shouldn't happen
@ -54,7 +64,6 @@ public class CheckService extends Service {
//noinspection ConstantConditions
if (e.response() != null && e.response().code() == 401) {
// Don't consider 401 unsuccessful here
site.status = ServerStatus.OK;
site.reason = null;
} else {
site.status = ServerStatus.ERROR;
@ -70,7 +79,11 @@ public class CheckService extends Service {
// Not used
break;
}
LOG("%s error: %s", site.name, site.reason);
if (site.status != ServerStatus.OK) {
LOG("%s error: %s", site.name, site.reason);
showNotification(this, site);
}
}
private void updateStatus(ServerModel site) {
@ -93,6 +106,40 @@ public class CheckService extends Service {
.getBoolean("check_service_running", false);
}
public static void isAppOpen(Context context, boolean open) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit().putBoolean("is_app_open", open).commit();
}
public static boolean isAppOpen(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean("is_app_open", false);
}
private static void showNotification(Context context, ServerModel site) {
if (isAppOpen(context)) {
// Don't show notifications while the app is open
return;
}
final NotificationManagerCompat nm = NotificationManagerCompat.from(context);
final PendingIntent openIntent = PendingIntent.getActivity(context, 9669,
new Intent(context, ViewSiteActivity.class)
.putExtra("model", site)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_CANCEL_CURRENT);
final Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(site.name)
.setContentText(context.getString(R.string.something_wrong))
.setContentIntent(openIntent)
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_VIBRATE)
.build();
nm.notify(site.url, NOTI_ID, noti);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (isRunning(this)) {

View file

@ -3,6 +3,7 @@ package com.afollestad.nocknock.ui;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.ActivityOptions;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -10,6 +11,7 @@ import android.content.IntentFilter;
import android.graphics.Path;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
@ -22,7 +24,6 @@ import android.view.MenuItem;
import android.view.View;
import android.view.animation.PathInterpolator;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.inquiry.Inquiry;
import com.afollestad.materialdialogs.MaterialDialog;
@ -38,6 +39,7 @@ import com.afollestad.nocknock.views.DividerItemDecoration;
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener, View.OnClickListener, ServerAdapter.ClickListener {
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";
@ -90,12 +92,13 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
mFab.setOnClickListener(this);
Inquiry.init(this, DB_NAME, 1);
refreshModels();
}
@Override
protected void onResume() {
super.onResume();
CheckService.isAppOpen(this, true);
try {
final IntentFilter filter = new IntentFilter();
filter.addAction(CheckService.ACTION_CHECK_UPDATE);
@ -104,11 +107,16 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
} catch (Throwable t) {
t.printStackTrace();
}
refreshModels();
}
@Override
protected void onPause() {
super.onPause();
CheckService.isAppOpen(this, false);
NotificationManagerCompat.from(this).cancel(CheckService.NOTI_ID);
try {
unregisterReceiver(mReceiver);
} catch (Throwable t) {
@ -186,38 +194,52 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
ServerModel model = (ServerModel) data.getSerializableExtra("model");
mAdapter.add(model);
mEmptyText.setVisibility(View.GONE);
Inquiry.get().insertInto(SITES_TABLE_NAME, ServerModel.class)
.values(model)
.run(changed -> {
AlarmUtil.setSiteChecks(MainActivity.this, model);
checkSite(model);
});
final ServerModel model = (ServerModel) data.getSerializableExtra("model");
if (requestCode == ADD_SITE_RQ) {
mAdapter.add(model);
mEmptyText.setVisibility(View.GONE);
Inquiry.get().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);
});
}
}
}
private void removeSite(final int index, final ServerModel model) {
Inquiry.init(this, DB_NAME, 1);
new MaterialDialog.Builder(this)
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(getString(R.string.remove_site_prompt, model.name)))
.content(Html.fromHtml(context.getString(R.string.remove_site_prompt, model.name)))
.positiveText(R.string.remove)
.negativeText(android.R.string.cancel)
.onPositive((dialog, which) -> {
AlarmUtil.cancelSiteChecks(MainActivity.this, model);
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)
.where("_id = ?", model.id)
.run();
mAdapter.remove(index);
if (onRemoved != null)
onRemoved.run();
}).show();
}
private void checkSite(ServerModel model) {
startService(new Intent(this, CheckService.class)
public static void checkSite(Context context, ServerModel model) {
context.startService(new Intent(context, CheckService.class)
.putExtra(CheckService.MODEL_ID, model.id));
}
@ -230,13 +252,15 @@ public class MainActivity extends AppCompatActivity implements SwipeRefreshLayou
.negativeText(android.R.string.cancel)
.itemsCallback((dialog, itemView, which, text) -> {
if (which == 0) {
checkSite(model);
checkSite(MainActivity.this, model);
} else {
removeSite(index, model);
removeSite(MainActivity.this, model, null);
}
}).show();
} else {
Toast.makeText(this, "Coming soon", Toast.LENGTH_SHORT).show();
startActivityForResult(new Intent(this, ViewSiteActivity.class)
.putExtra("model", model), VIEW_SITE_RQ,
ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
}
}
}

View file

@ -0,0 +1,236 @@
package com.afollestad.nocknock.ui;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.Patterns;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
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.services.CheckService;
import com.afollestad.nocknock.util.TimeUtil;
import com.afollestad.nocknock.views.StatusImageView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* @author Aidan Follestad (afollestad)
*/
public class ViewSiteActivity extends AppCompatActivity implements View.OnClickListener, Toolbar.OnMenuItemClickListener {
private StatusImageView iconStatus;
private EditText inputName;
private EditText inputUrl;
private EditText inputCheckInterval;
private Spinner checkIntervalSpinner;
private TextView textLastCheckResult;
private TextView textNextCheck;
private ServerModel mModel;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.v("ViewSiteActivity", "Received " + intent.getAction());
final ServerModel model = (ServerModel) intent.getSerializableExtra("model");
if (model != null) {
mModel = model;
update();
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewsite);
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationOnClickListener(view -> finish());
toolbar.inflateMenu(R.menu.menu_viewsite);
toolbar.setOnMenuItemClickListener(this);
iconStatus = (StatusImageView) findViewById(R.id.iconStatus);
inputName = (EditText) findViewById(R.id.inputName);
inputUrl = (EditText) findViewById(R.id.inputUrl);
inputCheckInterval = (EditText) findViewById(R.id.checkIntervalInput);
checkIntervalSpinner = (Spinner) findViewById(R.id.checkIntervalSpinner);
textLastCheckResult = (TextView) findViewById(R.id.textLastCheckResult);
textNextCheck = (TextView) findViewById(R.id.textNextCheck);
ArrayAdapter<String> intervalOptionsAdapter = new ArrayAdapter<>(this, R.layout.list_item_spinner,
getResources().getStringArray(R.array.interval_options));
checkIntervalSpinner.setAdapter(intervalOptionsAdapter);
mModel = (ServerModel) getIntent().getSerializableExtra("model");
update();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null && intent.hasExtra("model")) {
mModel = (ServerModel) intent.getSerializableExtra("model");
update();
}
}
@SuppressLint("SetTextI18n")
private void update() {
final SimpleDateFormat df = new SimpleDateFormat("MMMM dd, hh:mm:ss a", Locale.getDefault());
iconStatus.setStatus(mModel.status);
inputName.setText(mModel.name);
inputUrl.setText(mModel.url);
if (mModel.lastCheck == 0) {
textLastCheckResult.setText(R.string.none);
} else {
switch (mModel.status) {
case ServerStatus.CHECKING:
textLastCheckResult.setText(R.string.checking_status);
break;
case ServerStatus.ERROR:
textLastCheckResult.setText(mModel.reason);
break;
case ServerStatus.OK:
textLastCheckResult.setText(R.string.everything_checks_out);
break;
case ServerStatus.WAITING:
textLastCheckResult.setText(R.string.waiting);
break;
}
}
if (mModel.checkInterval == 0) {
textNextCheck.setText(R.string.none_turned_off);
inputCheckInterval.setText("");
checkIntervalSpinner.setSelection(0);
} else {
long lastCheck = mModel.lastCheck;
if (lastCheck == 0) lastCheck = System.currentTimeMillis();
textNextCheck.setText(df.format(new Date(lastCheck + mModel.checkInterval)));
if (mModel.checkInterval >= TimeUtil.WEEK) {
inputCheckInterval.setText(Integer.toString((int) Math.ceil(((float) mModel.checkInterval / (float) TimeUtil.WEEK))));
checkIntervalSpinner.setSelection(3);
} else if (mModel.checkInterval >= TimeUtil.DAY) {
inputCheckInterval.setText(Integer.toString((int) Math.ceil(((float) mModel.checkInterval / (float) TimeUtil.DAY))));
checkIntervalSpinner.setSelection(2);
} else if (mModel.checkInterval >= TimeUtil.HOUR) {
inputCheckInterval.setText(Integer.toString((int) Math.ceil(((float) mModel.checkInterval / (float) TimeUtil.HOUR))));
checkIntervalSpinner.setSelection(1);
} else if (mModel.checkInterval >= TimeUtil.MINUTE) {
inputCheckInterval.setText(Integer.toString((int) Math.ceil(((float) mModel.checkInterval / (float) TimeUtil.MINUTE))));
checkIntervalSpinner.setSelection(0);
} else {
inputCheckInterval.setText("0");
checkIntervalSpinner.setSelection(0);
}
}
findViewById(R.id.doneBtn).setOnClickListener(this);
}
@Override
protected void onResume() {
super.onResume();
try {
final IntentFilter filter = new IntentFilter();
filter.addAction(CheckService.ACTION_CHECK_UPDATE);
// filter.addAction(CheckService.ACTION_RUNNING);
registerReceiver(mReceiver, filter);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override
protected void onPause() {
super.onPause();
try {
unregisterReceiver(mReceiver);
} catch (Throwable t) {
t.printStackTrace();
}
}
// Save button
@Override
public void onClick(View view) {
mModel.name = inputName.getText().toString().trim();
mModel.url = inputUrl.getText().toString().trim();
mModel.status = ServerStatus.WAITING;
if (mModel.name.isEmpty()) {
inputName.setError(getString(R.string.please_enter_name));
return;
} else {
inputName.setError(null);
}
if (mModel.url.isEmpty()) {
inputUrl.setError(getString(R.string.please_enter_url));
return;
} else {
inputUrl.setError(null);
if (!Patterns.WEB_URL.matcher(mModel.url).find()) {
inputUrl.setError(getString(R.string.please_enter_valid_url));
return;
}
}
String intervalStr = inputCheckInterval.getText().toString().trim();
if (intervalStr.isEmpty()) intervalStr = "0";
mModel.checkInterval = Integer.parseInt(intervalStr);
switch (checkIntervalSpinner.getSelectedItemPosition()) {
case 0: // minutes
mModel.checkInterval *= (60 * 1000);
break;
case 1: // hours
mModel.checkInterval *= (60 * 60 * 1000);
break;
case 2: // days
mModel.checkInterval *= (60 * 60 * 24 * 1000);
break;
default: // weeks
mModel.checkInterval *= (60 * 60 * 24 * 7 * 1000);
break;
}
mModel.lastCheck = System.currentTimeMillis() - mModel.checkInterval;
setResult(RESULT_OK, new Intent().putExtra("model", mModel));
finish();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh:
MainActivity.checkSite(this, mModel);
return true;
case R.id.remove:
MainActivity.removeSite(this, mModel, () -> finish());
return true;
}
return false;
}
}

View file

@ -39,6 +39,7 @@ public class AlarmUtil {
public static void setSiteChecks(Context context, ServerModel site) {
cancelSiteChecks(context, site);
if (site.checkInterval <= 0) return;
if (site.lastCheck <= 0)
site.lastCheck = System.currentTimeMillis();
final long nextCheck = site.lastCheck + site.checkInterval;
@ -46,7 +47,8 @@ public class AlarmUtil {
final PendingIntent serviceIntent = getSiteIntent(context, site);
aMgr.setRepeating(AlarmManager.RTC_WAKEUP, nextCheck, site.checkInterval, serviceIntent);
final SimpleDateFormat df = new SimpleDateFormat("EEE MMM dd hh:mm:ssa z yyyy", Locale.getDefault());
Log.d("AlarmUtil", String.format(Locale.getDefault(), "Set site check alarm for %s (%s), next check: %s", site.name, site.url, df.format(new Date(nextCheck))));
Log.d("AlarmUtil", String.format(Locale.getDefault(), "Set site check alarm for %s (%s), check interval: %d, next check: %s",
site.name, site.url, site.checkInterval, df.format(new Date(nextCheck))));
}
public static void setSiteChecks(Context context, ServerModel[] sites) {

View file

@ -5,24 +5,26 @@ package com.afollestad.nocknock.util;
*/
public class TimeUtil {
private static long SECOND = 1000;
private static long MINUTE = SECOND * 60;
private static long HOUR = MINUTE * 60;
private static long DAY = HOUR * 24;
private static long WEEK = DAY * 7;
private static long MONTH = WEEK * 4;
public final static long SECOND = 1000;
public final static long MINUTE = SECOND * 60;
public final static long HOUR = MINUTE * 60;
public final static long DAY = HOUR * 24;
public final static long WEEK = DAY * 7;
public final static long MONTH = WEEK * 4;
public static String str(long duration) {
if (duration >= MONTH) {
return (duration / MONTH) + "mo";
if (duration <= 0) {
return "";
} else if (duration >= MONTH) {
return (int) Math.ceil(((float) duration / (float) MONTH)) + "mo";
} else if (duration >= WEEK) {
return (duration / WEEK) + "w";
return (int) Math.ceil(((float) duration / (float) WEEK)) + "w";
} else if (duration >= DAY) {
return (duration / DAY) + "d";
return (int) Math.ceil(((float) duration / (float) DAY)) + "d";
} else if (duration >= HOUR) {
return (duration / HOUR) + "h";
return (int) Math.ceil(((float) duration / (float) HOUR)) + "h";
} else if (duration >= MINUTE) {
return (duration / MINUTE) + "m";
return (int) Math.ceil(((float) duration / (float) MINUTE)) + "m";
} else {
return "<1m";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#fff"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#fff"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
</vector>

View file

@ -44,7 +44,8 @@
android:hint="@string/site_name"
android:inputType="textPersonName|textCapWords|textAutoCorrect"
android:textColor="?android:textColorPrimary"
android:textColorHint="?android:textColorSecondary" />
android:textColorHint="?android:textColorSecondary"
android:textSize="@dimen/body_font_size" />
</android.support.design.widget.TextInputLayout>
@ -63,54 +64,49 @@
android:hint="@string/site_url"
android:inputType="textUri"
android:textColor="?android:textColorPrimary"
android:textColorHint="?android:textColorSecondary" />
android:textColorHint="?android:textColorSecondary"
android:textSize="@dimen/body_font_size" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/checkIntervalLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_text_spacing"
android:layout_marginTop="@dimen/content_inset_more"
android:fontFamily="sans-serif"
android:text="@string/check_interval"
android:textColor="?colorAccent"
android:textSize="@dimen/caption_font_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/content_inset_more"
android:orientation="vertical">
android:orientation="horizontal"
android:weightSum="2">
<TextView
android:id="@+id/checkIntervalLabel"
android:layout_width="wrap_content"
<EditText
android:id="@+id/checkIntervalInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_text_spacing"
android:fontFamily="sans-serif"
android:text="@string/check_interval"
android:textColor="?colorAccent"
android:textSize="@dimen/caption_font_size" />
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="@dimen/content_inset_half"
android:layout_marginStart="-4dp"
android:layout_weight="1"
android:fontFamily="sans-serif-light"
android:hint="0"
android:inputType="number"
android:textSize="@dimen/body_font_size"
tools:ignore="HardcodedText,LabelFor" />
<LinearLayout
android:layout_width="match_parent"
<Spinner
android:id="@+id/checkIntervalSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<EditText
android:id="@+id/checkIntervalInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="@dimen/content_inset_half"
android:layout_marginStart="-4dp"
android:layout_weight="1"
android:fontFamily="sans-serif-light"
android:hint="@string/never_refresh"
android:inputType="number"
tools:ignore="LabelFor" />
<Spinner
android:id="@+id/checkIntervalSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="-4dp"
android:layout_weight="1" />
</LinearLayout>
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="-4dp"
android:layout_weight="1" />
</LinearLayout>

View file

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorPrimary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:navigationIcon="@drawable/ic_action_close"
app:title="@string/view_site"
app:titleTextColor="?android:textColorPrimary" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/content_inset"
android:paddingLeft="@dimen/content_inset"
android:paddingRight="@dimen/content_inset"
android:paddingTop="@dimen/content_inset_less">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.afollestad.nocknock.views.StatusImageView
android:id="@+id/iconStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_inset"
android:scaleType="centerInside"
android:transitionName="status_image_view"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/inputName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:hint="@string/site_name"
android:inputType="textPersonName|textCapWords|textAutoCorrect"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textColorHint="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
android:transitionName="site_name" />
<EditText
android:id="@+id/inputUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:hint="@string/site_url"
android:inputType="textUri"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textColorHint="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
android:transitionName="site_url" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="@dimen/content_inset_more"
android:background="#37474F" />
<TextView
android:id="@+id/checkIntervalLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_text_spacing"
android:layout_marginTop="@dimen/content_inset_more"
android:fontFamily="sans-serif"
android:text="@string/check_interval"
android:textColor="?colorAccent"
android:textSize="@dimen/caption_font_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<EditText
android:id="@+id/checkIntervalInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_marginEnd="@dimen/content_inset_half"
android:layout_marginStart="-4dp"
android:layout_weight="1"
android:fontFamily="sans-serif-light"
android:hint="0"
android:inputType="number"
android:textSize="@dimen/body_font_size"
tools:ignore="HardcodedText,LabelFor" />
<Spinner
android:id="@+id/checkIntervalSpinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="-4dp"
android:layout_weight="1" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/content_inset_more"
android:text="@string/last_check_result"
android:textColor="?colorAccent"
android:textSize="@dimen/caption_font_size" />
<TextView
android:id="@+id/textLastCheckResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_text_spacing"
android:fontFamily="sans-serif"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/medium_text_size"
tools:text="Everything checks out!" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/content_inset_more"
android:text="@string/next_check"
android:textColor="?colorAccent"
android:textSize="@dimen/caption_font_size" />
<TextView
android:id="@+id/textNextCheck"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_text_spacing"
android:fontFamily="sans-serif"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/medium_text_size"
tools:text="In 2 hours" />
<Button
android:id="@+id/doneBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:layout_marginTop="@dimen/content_inset_double"
android:text="@string/save"
android:textColor="#fff" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View file

@ -17,6 +17,7 @@
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/content_inset"
android:scaleType="centerInside"
android:transitionName="status_image_view"
tools:ignore="ContentDescription" />
<LinearLayout
@ -41,6 +42,7 @@
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/title_font_size"
android:transitionName="site_name"
tools:text="Website Name" />
<TextView
@ -66,6 +68,7 @@
android:singleLine="true"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
android:transitionName="site_url"
tools:text="https://yourwebsitehere.com" />
<TextView

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/refresh"
android:icon="@drawable/ic_action_refresh"
android:title="@string/refresh_status"
app:showAsAction="ifRoom" />
<item
android:id="@+id/remove"
android:icon="@drawable/ic_action_delete"
android:title="@string/remove_site"
app:showAsAction="ifRoom" />
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -2,6 +2,7 @@
<dimen name="headline_font_size">24sp</dimen>
<dimen name="title_font_size">20sp</dimen>
<dimen name="medium_text_size">16sp</dimen>
<dimen name="body_font_size">14sp</dimen>
<dimen name="caption_font_size">12sp</dimen>
@ -12,7 +13,7 @@
<dimen name="content_inset_double">32dp</dimen>
<dimen name="list_circle_size">42dp</dimen>
<dimen name="list_text_spacing">2dp</dimen>
<dimen name="list_text_spacing">4dp</dimen>
<dimen name="fab_elevation">4dp</dimen>
<dimen name="fab_elevation_pressed">8dp</dimen>
<dimen name="button_height">52dp</dimen>

View file

@ -24,7 +24,6 @@
<string name="site_url">Site URL</string>
<string name="check_interval">Check Interval</string>
<string name="done">Done</string>
<string name="never_refresh">Never Refresh</string>
<string name="please_enter_name">Please enter a name!</string>
<string name="please_enter_url">Please enter a URL.</string>
<string name="please_enter_valid_url">Please enter a valid URL.</string>
@ -35,6 +34,15 @@
<string name="remove_site">Remove Site</string>
<string name="remove_site_prompt"><![CDATA[Remove <b>%1$s</b> from your sites?]]></string>
<string name="remove">Remove</string>
<string name="save">Save</string>
<string name="view_site">View Site</string>
<string name="last_check_result">Last Check Result</string>
<string name="next_check">Next Check</string>
<string name="none_turned_off">None (turned off)</string>
<string name="none">None</string>
<string name="refresh_status">Refresh Status</string>
<string-array name="interval_options">
<item>Minute(s)</item>
<item>Hour(s)</item>
@ -42,9 +50,9 @@
<item>Weeks(s)</item>
</string-array>
<string-array name="site_long_options">
<item>Refresh Status</item>
<item>Remove Site</item>
<string-array name="site_long_options" translatable="false">
<item>@string/refresh_status</item>
<item>@string/remove_site</item>
</string-array>
</resources>

View file

@ -12,14 +12,16 @@
<item name="android:textColorSecondary">#727272</item>
</style>
<style name="AppTheme.Transparent" parent="Theme.AppCompat.NoActionBar">
<style name="AppTheme.Ink" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorButtonNormal">@color/colorPrimaryDark</item>
<item name="android:listDivider">@drawable/divider</item>
</style>
<style name="AppTheme.Transparent" parent="AppTheme.Ink">
<item name="android:windowIsTranslucent">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowBackground">@android:color/transparent</item>

BIN
web_hi_res_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB