├── Devnull
├── .gitignore
├── src
│ └── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── styles.xml
│ │ │ ├── dimens.xml
│ │ │ └── strings.xml
│ │ ├── layout
│ │ │ ├── main_activity.xml
│ │ │ ├── progress_dialog_fragment.xml
│ │ │ └── main_fragment.xml
│ │ └── values-w820dp
│ │ │ └── dimens.xml
│ │ ├── java
│ │ └── com
│ │ │ └── artemzin
│ │ │ └── android
│ │ │ └── dev_null
│ │ │ ├── api
│ │ │ ├── network
│ │ │ │ ├── NetworkException.java
│ │ │ │ └── NetworkRequest.java
│ │ │ ├── dev_null
│ │ │ │ └── DevNullApi.java
│ │ │ └── Util.java
│ │ │ ├── util
│ │ │ ├── ActivityUtil.java
│ │ │ ├── ThreadUtil.java
│ │ │ ├── StringUtil.java
│ │ │ └── TextValidator.java
│ │ │ ├── activity
│ │ │ └── MainActivity.java
│ │ │ └── fragment
│ │ │ ├── ProgressDialogFragment.java
│ │ │ └── MainFragment.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── settings.gradle
├── Devnull.apk
├── .gitignore
├── README.md
└── LICENSE
/Devnull/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':Devnull'
2 |
--------------------------------------------------------------------------------
/Devnull.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull.apk
--------------------------------------------------------------------------------
/Devnull/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/Devnull/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Devnull/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Devnull/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Devnull/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/artem-zinnatullin/dev-null-android/HEAD/Devnull/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Devnull/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Devnull/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/api/network/NetworkException.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.api.network;
2 |
3 | /**
4 | * @author Artem Zinnatullin [artem.zinnatullin@gmail.com]
5 | */
6 | public class NetworkException extends Exception {
7 |
8 | public NetworkException(String message) {
9 | super(message);
10 | }
11 | }
--------------------------------------------------------------------------------
/Devnull/src/main/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/Devnull/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | # *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 | out/
15 | target/
16 |
17 | # Local configuration file (sdk path, etc)
18 | local.properties
19 |
20 | # Eclipse project files
21 | .classpath
22 | .project
23 |
24 | # Idea IDE files
25 | *.idea/
26 | *.iml
27 |
28 | .gradle/
29 | gradle/
30 | gradlew
31 | gradlew.bat
--------------------------------------------------------------------------------
/Devnull/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:0.6.+'
7 | }
8 | }
9 | apply plugin: 'android'
10 |
11 | repositories {
12 | mavenCentral()
13 | }
14 |
15 | android {
16 | compileSdkVersion 18
17 | buildToolsVersion "18.1.0"
18 |
19 | defaultConfig {
20 | minSdkVersion 14
21 | targetSdkVersion 18
22 | }
23 | }
24 |
25 | dependencies {
26 | }
27 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/util/ActivityUtil.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.util;
2 |
3 | import android.app.Activity;
4 | import android.view.inputmethod.InputMethodManager;
5 |
6 | public class ActivityUtil {
7 |
8 | private ActivityUtil() {}
9 |
10 | public static void hideSoftKeyboard(Activity activity) {
11 | try {
12 | ((InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE))
13 | .hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
14 | } catch (Exception e) {
15 | // do nothing
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Devnull/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dev null
5 | NULL IT!
6 | Settings
7 | Null
8 |
9 | Enter text to null it!
10 |
11 | Nulling your data…
12 | Your data was nulled successfully!
13 | Can not null your data, network problem
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/api/dev_null/DevNullApi.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.api.dev_null;
2 |
3 | import com.artemzin.android.dev_null.api.network.NetworkException;
4 | import com.artemzin.android.dev_null.api.network.NetworkRequest;
5 |
6 | public class DevNullApi {
7 |
8 | private static final String BASE_URL = "http://devnull-as-a-service.com/";
9 |
10 | private DevNullApi() {}
11 |
12 | /**
13 | * F*ck, null() is deprecated method name in Java!
14 | * @return
15 | */
16 | public static String nullIt(String text) throws NetworkException {
17 | return NetworkRequest.newPostRequestInstance(BASE_URL + "dev/null", text).getResponse();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.activity;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 |
6 | import com.artemzin.android.dev_null.R;
7 | import com.artemzin.android.dev_null.fragment.MainFragment;
8 |
9 | public class MainActivity extends Activity {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.main_activity);
15 |
16 | if (savedInstanceState == null) {
17 | getFragmentManager().beginTransaction()
18 | .add(R.id.container, new MainFragment())
19 | .commit();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ###Dev null as Service comes to android!
2 |
3 | #####What is Dev-Null-As-Service? Here is the link: http://devnull-as-a-service.com/home/
4 |
5 | You can download Devnull.apk from repo or build it from sources and enjoy nulling texts from your android! You can use share intent for it!
6 |
7 | ####Screenshots:
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Devnull/src/main/res/layout/progress_dialog_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
14 |
15 |
23 |
24 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/util/ThreadUtil.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.util;
2 |
3 | import android.os.SystemClock;
4 |
5 | public class ThreadUtil {
6 |
7 | private ThreadUtil() {}
8 |
9 | /**
10 | * If action happens faster then required it will call sleep for the thread
11 | * Usefully for better user experience from UI
12 | * @param startTimeInMillis please use SystemClock.elapsedRealtime() to prevent bug when user changing datetime in system
13 | * @param minDurationInMillis action's minimal required duration in millis
14 | */
15 | public static void sleepIfRequired(long startTimeInMillis, long minDurationInMillis) {
16 | final long realDurationInMillis = SystemClock.elapsedRealtime() - startTimeInMillis;
17 |
18 | if (realDurationInMillis < minDurationInMillis - 100) {
19 | SystemClock.sleep(minDurationInMillis - realDurationInMillis);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Artem
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/api/Util.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.api;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.InputStreamReader;
6 | import java.io.StringWriter;
7 |
8 | public class Util {
9 |
10 | private Util() {}
11 |
12 | /**
13 | * Converting input stream content to string
14 | * @param is input stream to convert
15 | * @return String with stream content
16 | * @throws java.io.IOException if problems with reading input stream
17 | */
18 | public static String convertStreamToString(InputStream is) throws IOException {
19 | InputStreamReader r = new InputStreamReader(is);
20 | StringWriter sw = new StringWriter();
21 | char[] buffer = new char[1024];
22 | try {
23 | for (int n; (n = r.read(buffer)) != -1;)
24 | sw.write(buffer, 0, n);
25 | }
26 | finally{
27 | try {
28 | is.close();
29 | } catch (IOException e1) {
30 | e1.printStackTrace();
31 | }
32 | }
33 | return sw.toString();
34 | }
35 | }
--------------------------------------------------------------------------------
/Devnull/src/main/res/layout/main_fragment.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
15 |
16 |
21 |
22 |
27 |
28 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Devnull/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/fragment/ProgressDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.fragment;
2 |
3 | import android.app.DialogFragment;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.view.Window;
9 | import android.widget.TextView;
10 |
11 | import com.artemzin.android.dev_null.R;
12 |
13 | public class ProgressDialogFragment extends DialogFragment {
14 |
15 | private static final String ARGS_PROGRESS_TEXT_ID = "ARGS_PROGRESS_TEXT_ID";
16 |
17 | private String progressMessage;
18 |
19 | public static ProgressDialogFragment newInstance(String progressMessage) {
20 | Bundle args = new Bundle();
21 | args.putString(ARGS_PROGRESS_TEXT_ID, progressMessage);
22 |
23 | ProgressDialogFragment progressDialogFragment = new ProgressDialogFragment();
24 | progressDialogFragment.setArguments(args);
25 |
26 | return progressDialogFragment;
27 | }
28 |
29 | @Override
30 | public void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | setRetainInstance(true);
33 | progressMessage = getArguments().getString(ARGS_PROGRESS_TEXT_ID);
34 | }
35 |
36 | @Override
37 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
38 | getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); // hiding title from dialog window
39 | return inflater.inflate(R.layout.progress_dialog_fragment, null);
40 | }
41 |
42 | @Override
43 | public void onViewCreated(View view, Bundle savedInstanceState) {
44 | super.onViewCreated(view, savedInstanceState);
45 |
46 | ((TextView) view.findViewById(R.id.progress_dialog_message)).setText(progressMessage);
47 | }
48 |
49 | @Override
50 | public void onDestroyView() {
51 | if (getDialog() != null && getRetainInstance()) {
52 | getDialog().setDismissMessage(null);
53 | }
54 |
55 | super.onDestroyView();
56 | }
57 |
58 | @Override
59 | public void dismiss() {
60 | try {
61 | super.dismiss();
62 | } catch (Exception e) {
63 | }
64 | }
65 |
66 | @Override
67 | public void dismissAllowingStateLoss() {
68 | try {
69 | super.dismissAllowingStateLoss();
70 | } catch (Exception e) {
71 |
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/util/StringUtil.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.util;
2 |
3 | public class StringUtil {
4 |
5 | private StringUtil() {}
6 |
7 | public static boolean isNullOrEmpty(String string) {
8 | return string == null || string.isEmpty();
9 | }
10 |
11 | public static String getOnlyDigitsFromString(String textWithDigits) {
12 | if (isNullOrEmpty(textWithDigits)) {
13 | return "";
14 | }
15 | return textWithDigits.replaceAll("\\D", "");
16 | }
17 |
18 | public static String getOnlyUserAddedTextInsteadOfSharps(String pattern, String text) {
19 | final StringBuilder stringBuilder = new StringBuilder();
20 |
21 | for (int i = 0; i < pattern.length(); i++) {
22 | if (i >= text.length()) break;
23 |
24 | if (pattern.charAt(i) == '#') {
25 | stringBuilder.append(text.charAt(i));
26 | }
27 | }
28 |
29 | return stringBuilder.toString();
30 | }
31 |
32 | public static String insertSymbolsInsteadSharps(final String pattern, final String text, final boolean stopWhenTextFullyInserted) {
33 | final StringBuilder formattedText = new StringBuilder();
34 |
35 | int charPosFromTextToInsert = 0;
36 |
37 | for (int i = 0; i < pattern.length(); i++) {
38 | if (stopWhenTextFullyInserted && charPosFromTextToInsert > text.length() - 1) break; // No more symbols to insert
39 |
40 | if (pattern.charAt(i) == '#' && charPosFromTextToInsert < text.length()) {
41 | formattedText.append(text.charAt(charPosFromTextToInsert));
42 | charPosFromTextToInsert++;
43 | } else
44 | formattedText.append(pattern.charAt(i));
45 | }
46 |
47 | return formattedText.toString();
48 | }
49 |
50 | public static String getPatternPrefix(String pattern) {
51 | final StringBuilder stringBuilder = new StringBuilder();
52 |
53 | for (int i = 0; i < pattern.length(); i++) {
54 | if (pattern.charAt(i) == '{' && pattern.length() > i + 1) {
55 | for (int j = i + 1; j < pattern.length(); j++) {
56 | if (pattern.charAt(j) == '}') {
57 | return stringBuilder.toString();
58 | }
59 |
60 | stringBuilder.append(pattern.charAt(j));
61 | }
62 | }
63 | }
64 |
65 | return stringBuilder.toString();
66 | }
67 |
68 | public static String getPatternFormattingPart(String pattern) {
69 | for (int i = 0; i < pattern.length(); i++) {
70 | if (pattern.charAt(i) == '}') return pattern.substring(i + 1);
71 | }
72 |
73 | return pattern;
74 | }
75 |
76 | public static String substringWithoutException(String text, int startIndex, int endIndex) {
77 | if (isNullOrEmpty(text) || startIndex >= text.length()) {
78 | return "";
79 | }
80 |
81 | final StringBuilder stringBuilder = new StringBuilder();
82 | final int realEndIndex = endIndex >= text.length() ? text.length() - 1 : endIndex;
83 |
84 |
85 | for (int i = startIndex; i <= realEndIndex; i++) {
86 | stringBuilder.append(text.charAt(i));
87 | }
88 |
89 | return stringBuilder.toString();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/util/TextValidator.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.util;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import java.util.LinkedList;
9 | import java.util.List;
10 |
11 | public abstract class TextValidator implements TextWatcher, View.OnFocusChangeListener {
12 |
13 | private final TextView textView;
14 | private boolean validateAfterTextChanged, validateOnFocusChange;
15 |
16 | private Boolean isValid;
17 |
18 | private final List onValidationChangedListeners = new LinkedList();
19 |
20 | public TextValidator(TextView textView) {
21 | this(textView, true, true);
22 | }
23 |
24 | public TextValidator(TextView textView, boolean validateAfterTextChanged, boolean validateOnFocusChange) {
25 | this.textView = textView;
26 | textView.addTextChangedListener(this);
27 | this.validateAfterTextChanged = validateAfterTextChanged;
28 | this.validateOnFocusChange = validateOnFocusChange;
29 |
30 | if (validateOnFocusChange) {
31 | textView.setOnFocusChangeListener(this);
32 | }
33 | }
34 |
35 | public TextView getTextView() {
36 | return textView;
37 | }
38 |
39 | protected boolean isValidateAfterTextChanged() {
40 | return validateAfterTextChanged;
41 | }
42 |
43 | protected boolean isValidateOnFocusChange() {
44 | return validateOnFocusChange;
45 | }
46 |
47 | public Boolean isValid() {
48 | return isValid;
49 | }
50 |
51 | private void setIsValid(String validationError, boolean show) {
52 | isValid = isNullOrEmpty(validationError);
53 | if (show) {
54 | textView.setError(validationError);
55 | } else {
56 | textView.setError(null);
57 | }
58 | }
59 |
60 | protected abstract String validate(String text);
61 |
62 | /**
63 | * Force call validation for the TextView, OnValidationChangedListeners will be notified with the result of validation
64 | * @param showError if true and validation fails then the error message is displayed
65 | * @return true if validation succeed, false otherwise
66 | */
67 | public final boolean validate(boolean showError) {
68 | setIsValid(validate(textView.getText().toString()), showError);
69 |
70 | return isValid;
71 | }
72 |
73 | @Override
74 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
75 | }
76 |
77 | @Override
78 | public void onTextChanged(CharSequence s, int start, int before, int count) {
79 | }
80 |
81 | @Override
82 | public void afterTextChanged(Editable s) {
83 | if (validateAfterTextChanged) {
84 | final String text = textView.getText().toString();
85 | final String validationError = validate(text);
86 | setIsValid(validationError, false);
87 |
88 | notifyOnValidationChangedListeners(isValid, text, validationError);
89 | }
90 | }
91 |
92 | @Override
93 | public void onFocusChange(View v, boolean hasFocus) {
94 | if (validateOnFocusChange && !hasFocus) {
95 | final String text = textView.getText().toString();
96 | final String validationError = validate(text);
97 | setIsValid(validationError, true);
98 |
99 | notifyOnValidationChangedListeners(isValid, text, validationError);
100 | }
101 | }
102 |
103 | protected static boolean isNullOrEmpty(String string) {
104 | return StringUtil.isNullOrEmpty(string);
105 | }
106 |
107 | public final void addOnValidationChangedListener(OnValidationChangedListener listener) {
108 | onValidationChangedListeners.add(listener);
109 | }
110 |
111 | public final boolean removeOnValidationChangedListener(OnValidationChangedListener listener) {
112 | return onValidationChangedListeners.remove(listener);
113 | }
114 |
115 | private final void notifyOnValidationChangedListeners(boolean isValid, String textValue, String validationErrorText) {
116 | for (OnValidationChangedListener listener : onValidationChangedListeners) {
117 | if (listener != null) listener.onValidationChanged(isValid, textValue, validationErrorText);
118 | }
119 | }
120 |
121 | public interface OnValidationChangedListener {
122 | void onValidationChanged(boolean isValid, String text, String validationError);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/fragment/MainFragment.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.fragment;
2 |
3 | import android.app.Fragment;
4 | import android.content.Intent;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.os.SystemClock;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.Button;
12 | import android.widget.EditText;
13 | import android.widget.Toast;
14 |
15 | import com.artemzin.android.dev_null.R;
16 | import com.artemzin.android.dev_null.api.dev_null.DevNullApi;
17 | import com.artemzin.android.dev_null.api.network.NetworkException;
18 | import com.artemzin.android.dev_null.util.ActivityUtil;
19 | import com.artemzin.android.dev_null.util.StringUtil;
20 | import com.artemzin.android.dev_null.util.TextValidator;
21 | import com.artemzin.android.dev_null.util.ThreadUtil;
22 |
23 | public class MainFragment extends Fragment {
24 |
25 | private static final String STATE_BUNDLE_NULL_IT_TEXT = "STATE_BUNDLE_NULL_IT_TEXT";
26 |
27 | private EditText nullItEditText;
28 | private Button nullItButton;
29 |
30 | private Intent lastIntent;
31 |
32 | @Override
33 | public void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setRetainInstance(true);
36 | }
37 |
38 | @Override
39 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
40 | return inflater.inflate(R.layout.main_fragment, null);
41 | }
42 |
43 | @Override
44 | public void onViewCreated(View view, Bundle savedInstanceState) {
45 | super.onViewCreated(view, savedInstanceState);
46 |
47 | nullItEditText = (EditText) view.findViewById(R.id.null_it_edit_text);
48 | nullItButton = (Button) view.findViewById(R.id.null_it_button);
49 | nullItButton.setEnabled(false);
50 |
51 | new TextValidator(nullItEditText) {
52 |
53 | @Override
54 | protected String validate(String text) {
55 | if (StringUtil.isNullOrEmpty(text)) {
56 | return getString(R.string.null_it_edit_text_validation_error_empty);
57 | }
58 |
59 | return null;
60 | }
61 | }.addOnValidationChangedListener(new TextValidator.OnValidationChangedListener() {
62 | @Override
63 | public void onValidationChanged(boolean isValid, String text, String validationError) {
64 | nullItButton.setEnabled(isValid);
65 | }
66 | });
67 |
68 | nullItButton.setOnClickListener(new View.OnClickListener() {
69 | @Override
70 | public void onClick(View v) {
71 | ActivityUtil.hideSoftKeyboard(getActivity());
72 |
73 | final ProgressDialogFragment progressDialogFragment = ProgressDialogFragment.newInstance(getString(R.string.progress_dialog_nulling));
74 | progressDialogFragment.setCancelable(false);
75 | progressDialogFragment.show(getFragmentManager(), "progressDialogFragment");
76 |
77 | final long requestStartTime = SystemClock.elapsedRealtime();
78 |
79 | new AsyncTask() {
80 |
81 | private NetworkException networkException;
82 |
83 | @Override
84 | protected Void doInBackground(Void... params) {
85 |
86 | try {
87 | DevNullApi.nullIt(nullItEditText.getText().toString());
88 | } catch (NetworkException e) {
89 | networkException = e;
90 | }
91 |
92 | // request duration should be at least 3 sec
93 | ThreadUtil.sleepIfRequired(requestStartTime, 3000);
94 |
95 | return null;
96 | }
97 |
98 | @Override
99 | protected void onPostExecute(Void aVoid) {
100 | progressDialogFragment.dismiss();
101 |
102 | if (networkException == null) {
103 | nullItEditText.setText(null); // /dev/null!!! local
104 | Toast.makeText(getActivity(), R.string.toast_null_success, Toast.LENGTH_LONG).show();
105 | } else {
106 | Toast.makeText(getActivity(), R.string.toast_null_network_error, Toast.LENGTH_LONG).show();
107 | }
108 | }
109 | }.execute();
110 | }
111 | });
112 |
113 | if (savedInstanceState != null) {
114 | nullItEditText.setText(savedInstanceState.getString(STATE_BUNDLE_NULL_IT_TEXT, ""));
115 | }
116 | }
117 |
118 | @Override
119 | public void onStart() {
120 | super.onStart();
121 |
122 | final Intent intent = getActivity().getIntent();
123 |
124 | if (intent == null || intent.equals(lastIntent)) return;
125 |
126 | if ("android.intent.action.SEND".equals(intent.getAction())) {
127 | if ("text/plain".equals(intent.getType())) {
128 | nullItEditText.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
129 | nullItEditText.setSelection(nullItEditText.getText().toString().length());
130 | }
131 | }
132 | }
133 |
134 | @Override
135 | public void onSaveInstanceState(Bundle outState) {
136 | final String nullItText = nullItEditText.getText().toString();
137 |
138 | if (!StringUtil.isNullOrEmpty(nullItText)) {
139 | outState.putString(STATE_BUNDLE_NULL_IT_TEXT, nullItText);
140 | }
141 |
142 | super.onSaveInstanceState(outState);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/Devnull/src/main/java/com/artemzin/android/dev_null/api/network/NetworkRequest.java:
--------------------------------------------------------------------------------
1 | package com.artemzin.android.dev_null.api.network;
2 |
3 | import com.artemzin.android.dev_null.api.Util;
4 |
5 | import java.io.BufferedInputStream;
6 | import java.io.BufferedWriter;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.io.OutputStreamWriter;
10 | import java.net.HttpURLConnection;
11 | import java.net.MalformedURLException;
12 | import java.net.SocketException;
13 | import java.net.URL;
14 | import java.util.zip.GZIPInputStream;
15 |
16 | import javax.net.ssl.SSLException;
17 |
18 | /**
19 | * Network request object is needed to send requests to our servers
20 | * It handles all http problems and work for retrying request if something goes wrong
21 | * @author Artem Zinnatullin [artem.zinnatullin@gmail.com]
22 | */
23 | public class NetworkRequest {
24 |
25 | /**
26 | * Default connection timeout in millis
27 | * 30000 millis == 30 seconds
28 | */
29 | public static final int CONNECTION_TIMEOUT_IN_MILLIS_DEFAULT = 30000;
30 |
31 | /**
32 | * By default gzip compression is enabled
33 | */
34 | public static final boolean IS_GZIP_COMPRESSION_ENABLED_DEFAULT = true;
35 |
36 | /**
37 | * By default we will retry query one time
38 | * NOTICE: this is RETRY count
39 | * so if you will set for example = 2, total queries count could be 3,
40 | * because first is basic, then we could repeat it 2 times
41 | */
42 | public static final int QUERY_RETRY_LIMIT_DEFAULT = 1;
43 |
44 | int connectionTimeoutInMillis = CONNECTION_TIMEOUT_IN_MILLIS_DEFAULT;
45 | boolean isGzipCompressionEnabled = IS_GZIP_COMPRESSION_ENABLED_DEFAULT;
46 | int queryRetryLimit = QUERY_RETRY_LIMIT_DEFAULT;
47 |
48 | private final URL url;
49 | private final Method method;
50 | private final String postBody;
51 |
52 | public enum Method {
53 | POST,
54 | GET
55 | }
56 |
57 | /**
58 | * Creating network request object and checks entered url
59 | * @param url where you want to send request
60 | * @param method request method
61 | * @throws java.net.MalformedURLException if url format is incorrect
62 | */
63 | private NetworkRequest(String url, Method method, String postBody) throws NetworkException {
64 | try {
65 | this.url = new URL(url);
66 | } catch (MalformedURLException e) {
67 | throw new NetworkException("MalformedURLException: " + e.getMessage());
68 | }
69 |
70 | this.method = method;
71 | this.postBody = postBody;
72 | }
73 |
74 | public static NetworkRequest newGetRequestInstance(String url) throws NetworkException {
75 | return new NetworkRequest(url, Method.GET, null);
76 | }
77 |
78 | public static NetworkRequest newPostRequestInstance(String url, String postBody) throws NetworkException {
79 | return new NetworkRequest(url, Method.POST, postBody);
80 | }
81 |
82 | public int getConnectionTimeoutInMillis() {
83 | return connectionTimeoutInMillis;
84 | }
85 |
86 | public void setConnectionTimeoutInMillis(int connectionTimeoutInMillis) {
87 | this.connectionTimeoutInMillis = connectionTimeoutInMillis;
88 | }
89 |
90 | public boolean isGzipCompressionEnabled() {
91 | return isGzipCompressionEnabled;
92 | }
93 |
94 | public void setGzipCompressionEnabled(boolean gzipCompressionEnabled) {
95 | isGzipCompressionEnabled = gzipCompressionEnabled;
96 | }
97 |
98 | public int getQueryRetryLimit() {
99 | return queryRetryLimit;
100 | }
101 |
102 | public void setQueryRetryLimit(int queryRetryLimit) {
103 | this.queryRetryLimit = queryRetryLimit;
104 | }
105 |
106 | public String getUrl() {
107 | return url.toExternalForm();
108 | }
109 |
110 | /**
111 | * Sends request to needed url and returns response as string
112 | * @return server response as string
113 | * @throws NetworkException if some of network problems caused
114 | */
115 | public String getResponse() throws NetworkException {
116 | String response = null;
117 |
118 | for (int i = 0; i < queryRetryLimit; i++) {
119 | try {
120 | response = getInternalResponse();
121 | break;
122 | } catch (SSLException e) {
123 | if (i < queryRetryLimit - 1)
124 | continue;
125 | throw new NetworkException("SSL exception: " + e.getMessage());
126 | } catch (SocketException e) {
127 | if (i < queryRetryLimit - 1)
128 | continue;
129 | throw new NetworkException("Socket exception: " + e.getMessage());
130 | } catch (Exception e) {
131 | if (i < queryRetryLimit - 1)
132 | continue;
133 | throw new NetworkException("Network exception: " + e.getMessage());
134 | }
135 | }
136 |
137 | return response;
138 | }
139 |
140 | /**
141 | * Sends http request to needed url and returns result as string
142 | * @return response as string
143 | * @throws Exception if something going wrong
144 | */
145 | private String getInternalResponse() throws Exception {
146 | HttpURLConnection connection = null;
147 |
148 | try {
149 | // This is important, we could not use existing URL object because request could be repeated
150 | // So its state will be corrupted
151 | connection = (HttpURLConnection) new URL(url.toExternalForm()).openConnection();
152 |
153 | connection.setConnectTimeout(connectionTimeoutInMillis);
154 | connection.setReadTimeout(connectionTimeoutInMillis);
155 | connection.setUseCaches(false);
156 | connection.setDoInput(true);
157 |
158 | if (isGzipCompressionEnabled)
159 | connection.setRequestProperty("Accept-Encoding", "gzip");
160 |
161 | if (method.equals(Method.GET)) {
162 | connection.setRequestMethod("GET");
163 | connection.setDoOutput(false);
164 | } else if (method.equals(Method.POST)) {
165 | connection.setRequestMethod("POST");
166 |
167 | if (postBody != null) {
168 | connection.setDoOutput(true);
169 |
170 | final OutputStream outputStream = connection.getOutputStream();
171 | final BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
172 | bufferedWriter.write(postBody);
173 | bufferedWriter.close();
174 | }
175 | }
176 |
177 | final int responseCode = connection.getResponseCode();
178 | if (responseCode == -1)
179 | throw new Exception("Got response code -1, may be http keep-alive problem");
180 |
181 | InputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8192);
182 |
183 | final String contentEncoding = connection.getContentEncoding();
184 |
185 | if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip"))
186 | inputStream = new GZIPInputStream(inputStream);
187 |
188 | return Util.convertStreamToString(inputStream);
189 | }
190 | finally {
191 | if (connection != null)
192 | connection.disconnect();
193 | }
194 | }
195 | }
--------------------------------------------------------------------------------