demo/src/main/java/me/angrybyte/sillyandroid/demo/MainActivity.java
package me.angrybyte.sillyandroid.demo;
import android.Manifest;
import android.app.Dialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresPermission;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Date;
import java.util.Set;
import me.angrybyte.sillyandroid.SillyAndroid;
import me.angrybyte.sillyandroid.extras.Coloring;
import me.angrybyte.sillyandroid.parsable.Annotations.Clickable;
import me.angrybyte.sillyandroid.parsable.Annotations.FindView;
import me.angrybyte.sillyandroid.parsable.Annotations.Layout;
import me.angrybyte.sillyandroid.parsable.Annotations.LongClickable;
import me.angrybyte.sillyandroid.parsable.Annotations.Menu;
import me.angrybyte.sillyandroid.parsable.LayoutWrapper;
import me.angrybyte.sillyandroid.parsable.components.ParsableActivity;
/**
* The main activity of the demo app, parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseType(Context, Object)}.
*/
@SuppressWarnings("unused")
@Layout(R.layout.activity_main)
@Menu(R.menu.activity_main)
public final class MainActivity extends ParsableActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int DIALOG_DEMO = 0xD00001;
// <editor-fold desc="View bindings">
/**
* The main layout container. (no, you don't have to do this, it's just an example).
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@FindView(R.id.container_main)
private ViewGroup mMainContainer;
/**
* The main textual display TextView.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@FindView(R.id.display_text_view)
private TextView mDisplayView;
/**
* The "print info" button.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@Clickable
@FindView(R.id.button_print_info)
private Button mInfoButton;
/**
* The "hide keyboard" button.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@Clickable
@FindView(R.id.button_hide_keyboard)
private Button mHideKeyboardButton;
/**
* The "apply random padding" button.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@LongClickable
@FindView(R.id.button_random_padding)
private Button mPaddingButton;
/**
* The keyboard tester input field.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@FindView(R.id.edit_text_keyboard)
private EditText mEditText;
/**
* The managed dialog button.
* Parsed using {@link me.angrybyte.sillyandroid.parsable.AnnotationParser#parseFields(Context, Object, LayoutWrapper)}.
*/
@Clickable
@FindView(R.id.button_show_dialog)
private Button mManagedDialogButton;
// </editor-fold>
/**
* {@inheritDoc}
*/
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* Prepare the special button coloring to demonstrate the Coloring class (you would have something like this happen generically in real apps):
*
* - IDLE state colors -
* Background: GRAY
* Text: Contrast to [IDLE.Background]
* Icon: Contrast to [IDLE.Background]
*
* - PRESSED state colors -
* Background: #FFCC00 (bright yellow)
* Text: Contrast to [PRESSED.Background]
* Icon: Contrast to [PRESSED.Background]
*
* Android does not recolor these to contrast colors when pressed, so we're doing that manually below.
*/
final int idleBackgroundColor = Color.GRAY; // button background when not pressed
final int idleContentColor = Coloring.contrastColor(idleBackgroundColor); // text and icon color when not pressed
final int pressedBackgroundColor = 0xFFFFCC00; // button background highlight color when pressed
final Drawable originalDrawable = ContextCompat.getDrawable(this, android.R.drawable.star_big_on); // load a random icon from android
@SuppressWarnings("ConstantConditions") final StateListDrawable statefulDrawable = Coloring.createContrastStateDrawable(this, idleContentColor,
pressedBackgroundColor, true, originalDrawable);
final ColorStateList statefulTextColors = Coloring.createContrastTextColors(idleContentColor, pressedBackgroundColor);
final Rect originalBounds = mPaddingButton.getBackground().copyBounds(); // copy original drawable's bounds so that the ripple is bordered
final int cornerRoundness = SillyAndroid.convertDipsToPixels(this, 4);
final Drawable backgroundDrawable = Coloring.createResponsiveDrawable(this, idleBackgroundColor, pressedBackgroundColor, idleBackgroundColor, true,
cornerRoundness, originalBounds);
setBackgroundCompat(mPaddingButton, backgroundDrawable);
mPaddingButton.setCompoundDrawablesWithIntrinsicBounds(statefulDrawable, null, null, null);
mPaddingButton.setTextColor(statefulTextColors);
}
/**
* {@inheritDoc}
*/
@Override
public void onKeyboardShown(@IntRange(from = 1) final int size) {
super.onKeyboardShown(size);
mEditText.setHint(getString(R.string.hint_keyboard_size, String.valueOf(size)));
}
/**
* {@inheritDoc}
*/
@Override
public void onKeyboardHidden() {
super.onKeyboardHidden();
mEditText.setHint(getString(R.string.hint_keyboard_none));
}
/**
* {@inheritDoc}
*/
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_main_die: {
toastShort(R.string.toast_die_done);
finish();
return true;
}
default: {
return super.onOptionsItemSelected(item);
}
}
}
/**
* Gets the current device type, in text. One of {@link me.angrybyte.sillyandroid.SillyAndroid.UI.DeviceType} constants.
*
* @return The textual description of the current device
*/
@NonNull
private String getDeviceType() {
final @SillyAndroid.UI.DeviceType int type = SillyAndroid.UI.getDeviceType(this);
switch (type) {
case SillyAndroid.UI.PHONE_LAND:
return "Phone, landscape";
case SillyAndroid.UI.PHONE_PORT:
return "Phone, portrait";
case SillyAndroid.UI.TABLET_LAND:
return "Big tablet, landscape";
case SillyAndroid.UI.TABLET_PORT:
return "Big tablet, portrait";
case SillyAndroid.UI.TAB_LAND:
return "Small tablet, landscape";
case SillyAndroid.UI.TAB_PORT:
return "Small tablet, portrait";
case SillyAndroid.UI.TV:
return "Television";
case SillyAndroid.UI.WATCH:
return "Watch, wear device";
default:
return "WE DON'T KNOW!";
}
}
/**
* Displays some demo information on the {@link #mDisplayView}.
*/
@RequiresPermission(allOf = {Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_NETWORK_STATE})
private void printInfo() {
final StringBuilder builder = new StringBuilder();
// print current context
builder.append("Current ").append(Context.class.getSimpleName()).append(": ").append(this.toString()).append("\n");
// check instance of parsable layout wrapper
builder.append(LayoutWrapper.class.isAssignableFrom(getClass()) ? "Assignable from LayoutWrapper" : "Not assignable from LayoutWrapper");
// see DeviceType constants
builder.append("Device type: ").append(getDeviceType()).append("\n");
// screen size in pixels
final Point screenSize = SillyAndroid.UI.getScreenSize(this);
builder.append("Screen size: ").append(screenSize.x).append("x").append(screenSize.y).append("\n");
// density in DPI (rounded)
builder.append("Density DPI: ").append(SillyAndroid.UI.getDensityDpi(this)).append("\n");
// native 7.0+ multi-window mode
builder.append(SillyAndroid.UI.isInMultiWindowMode(this) ? "Multi-window mode" : "Single-window mode").append("\n");
// any network connection, doesn't mean you have Internet connection
builder.append("Network status: ").append(SillyAndroid.isNetworkConnected(this) ? "Connected" : "Disconnected").append("\n");
// checks the thread. setting anything to a TextView from a non-UI thread would crash
builder.append(SillyAndroid.isThisMainThread() ? "Running on UI thread" : "OMG! This is not running on a UI thread!");
final String displayText = builder.toString();
if (BuildConfig.DEBUG) {
Log.d(TAG, "printInfo: " + displayText);
}
mDisplayView.setText(displayText);
}
/**
* {@inheritDoc}
*/
@Nullable
@Override
public Dialog onCreateDialog(final int dialogId, @Nullable final Bundle config) {
switch (dialogId) {
case DIALOG_DEMO: {
// prepare the dialog, don't show it
final AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("A managed dialog")
.setNeutralButton("Yeah, ok", (d, which) -> d.dismiss())
.create();
final String message = "Managed dialog, ID = " + Integer.toHexString(DIALOG_DEMO) + "\n"
+ "HashCode = " + Integer.toHexString(dialog.hashCode()) + "\n"
+ "Config = " + String.valueOf(config) + "\n"
+ "Time created = " + new Date().toString();
dialog.setMessage(message);
// still don't call #show() on the dialog!
return dialog;
}
default:
return super.onCreateDialog(dialogId, config);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onClick(final View v) {
super.onClick(v);
switch (v.getId()) {
case R.id.button_print_info: {
// obviously you don't need to request these, but I need some demo code with permissions here..
if (!hasPermission(Manifest.permission.ACCESS_WIFI_STATE) || !hasPermission(Manifest.permission.ACCESS_NETWORK_STATE)) {
final boolean requested = requestPermissions(10, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_NETWORK_STATE);
if (!requested) {
Log.e(TAG, "onClick: Failed to request permissions");
}
return;
}
printInfo();
break;
}
case R.id.button_hide_keyboard: {
if (!hideKeyboard()) {
toastShort("Failed to hide keyboard, check the log");
}
break;
}
case R.id.button_show_dialog: {
getDialogManager().showDialog(DIALOG_DEMO);
break;
}
default: {
Log.w(TAG, "onClick: Unknown View clicked: " + getResources().getResourceName(v.getId()));
break;
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onPermissionsResult(@IntRange(from = 0, to = 127) final int code, @NonNull final Set<String> granted, @NonNull final Set<String> denied) {
super.onPermissionsResult(code, granted, denied);
switch (code) {
case 10: {
if (granted.contains(Manifest.permission.ACCESS_WIFI_STATE) && granted.contains(Manifest.permission.ACCESS_NETWORK_STATE)) {
printInfo();
} else {
toastLong(R.string.toast_permission_missing);
}
break;
}
default: {
Log.e(TAG, "onPermissionsResult: Unknown request code " + code);
break;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean onLongClick(final View v) {
switch (v.getId()) {
case R.id.button_random_padding: {
final int screenWidth = SillyAndroid.UI.getScreenSize(this).x;
// pick at random between 10% and 25% of the screen's width
final int randomVerticalPadding = (int) (screenWidth / 10 + Math.random() * (screenWidth / 4));
setPaddingVertical(mDisplayView, randomVerticalPadding);
return true;
}
default: {
return super.onLongClick(v);
}
}
}
}