diff --git a/.google/packaging.yaml b/.google/packaging.yaml index aad047a..a60d4a6 100644 --- a/.google/packaging.yaml +++ b/.google/packaging.yaml @@ -14,4 +14,6 @@ level: INTERMEDIATE icon: screenshots/icon-web.png apiRefs: - android:android.os.AsyncTask + - android:android.net.ConnectivityManager + - android:android.net.NetworkInfo license: apache2 diff --git a/Application/build.gradle b/Application/build.gradle index b6e6c7f..6c9529e 100644 --- a/Application/build.gradle +++ b/Application/build.gradle @@ -35,7 +35,7 @@ android { buildToolsVersion "25.0.2" defaultConfig { - minSdkVersion 8 + minSdkVersion 11 targetSdkVersion 25 } diff --git a/Application/src/main/AndroidManifest.xml b/Application/src/main/AndroidManifest.xml index 00ce7f3..654203b 100644 --- a/Application/src/main/AndroidManifest.xml +++ b/Application/src/main/AndroidManifest.xml @@ -31,8 +31,8 @@ android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/Theme.Sample" - android:allowBackup="true"> - + android:allowBackup="true" + android:networkSecurityConfig="@xml/network_security_config"> When this is set as the head of the list, - * an instance of it can function as a drop-in replacement for {@link android.util.Log}. - * Most of the methods in this class server only to map a method call in Log to its equivalent - * in LogNode.

- */ -public class Log { - // Grabbing the native values from Android's native logging facilities, - // to make for easy migration and interop. - public static final int NONE = -1; - public static final int VERBOSE = android.util.Log.VERBOSE; - public static final int DEBUG = android.util.Log.DEBUG; - public static final int INFO = android.util.Log.INFO; - public static final int WARN = android.util.Log.WARN; - public static final int ERROR = android.util.Log.ERROR; - public static final int ASSERT = android.util.Log.ASSERT; - - // Stores the beginning of the LogNode topology. - private static LogNode mLogNode; - - /** - * Returns the next LogNode in the linked list. - */ - public static LogNode getLogNode() { - return mLogNode; - } - - /** - * Sets the LogNode data will be sent to. - */ - public static void setLogNode(LogNode node) { - mLogNode = node; - } - - /** - * Instructs the LogNode to print the log data provided. Other LogNodes can - * be chained to the end of the LogNode as desired. - * - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void println(int priority, String tag, String msg, Throwable tr) { - if (mLogNode != null) { - mLogNode.println(priority, tag, msg, tr); - } - } - - /** - * Instructs the LogNode to print the log data provided. Other LogNodes can - * be chained to the end of the LogNode as desired. - * - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - */ - public static void println(int priority, String tag, String msg) { - println(priority, tag, msg, null); - } - - /** - * Prints a message at VERBOSE priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void v(String tag, String msg, Throwable tr) { - println(VERBOSE, tag, msg, tr); - } - - /** - * Prints a message at VERBOSE priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void v(String tag, String msg) { - v(tag, msg, null); - } - - - /** - * Prints a message at DEBUG priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void d(String tag, String msg, Throwable tr) { - println(DEBUG, tag, msg, tr); - } - - /** - * Prints a message at DEBUG priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void d(String tag, String msg) { - d(tag, msg, null); - } - - /** - * Prints a message at INFO priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void i(String tag, String msg, Throwable tr) { - println(INFO, tag, msg, tr); - } - - /** - * Prints a message at INFO priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void i(String tag, String msg) { - i(tag, msg, null); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void w(String tag, String msg, Throwable tr) { - println(WARN, tag, msg, tr); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void w(String tag, String msg) { - w(tag, msg, null); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void w(String tag, Throwable tr) { - w(tag, null, tr); - } - - /** - * Prints a message at ERROR priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void e(String tag, String msg, Throwable tr) { - println(ERROR, tag, msg, tr); - } - - /** - * Prints a message at ERROR priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void e(String tag, String msg) { - e(tag, msg, null); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void wtf(String tag, String msg, Throwable tr) { - println(ASSERT, tag, msg, tr); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void wtf(String tag, String msg) { - wtf(tag, msg, null); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void wtf(String tag, Throwable tr) { - wtf(tag, null, tr); - } -} diff --git a/Application/src/main/java/com/example/android/common/logger/LogFragment.java b/Application/src/main/java/com/example/android/common/logger/LogFragment.java deleted file mode 100644 index b302acd..0000000 --- a/Application/src/main/java/com/example/android/common/logger/LogFragment.java +++ /dev/null @@ -1,109 +0,0 @@ -/* -* Copyright 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.common.logger; - -import android.graphics.Typeface; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ScrollView; - -/** - * Simple fraggment which contains a LogView and uses is to output log data it receives - * through the LogNode interface. - */ -public class LogFragment extends Fragment { - - private LogView mLogView; - private ScrollView mScrollView; - - public LogFragment() {} - - public View inflateViews() { - mScrollView = new ScrollView(getActivity()); - ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mScrollView.setLayoutParams(scrollParams); - - mLogView = new LogView(getActivity()); - ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); - logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; - mLogView.setLayoutParams(logParams); - mLogView.setClickable(true); - mLogView.setFocusable(true); - mLogView.setTypeface(Typeface.MONOSPACE); - - // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! - int paddingDips = 16; - double scale = getResources().getDisplayMetrics().density; - int paddingPixels = (int) ((paddingDips * (scale)) + .5); - mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); - mLogView.setCompoundDrawablePadding(paddingPixels); - - mLogView.setGravity(Gravity.BOTTOM); - mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); - - mScrollView.addView(mLogView); - return mScrollView; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View result = inflateViews(); - - mLogView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - - @Override - public void afterTextChanged(Editable s) { - mScrollView.fullScroll(ScrollView.FOCUS_DOWN); - } - }); - return result; - } - - public LogView getLogView() { - return mLogView; - } -} \ No newline at end of file diff --git a/Application/src/main/java/com/example/android/common/logger/LogNode.java b/Application/src/main/java/com/example/android/common/logger/LogNode.java deleted file mode 100644 index bc37cab..0000000 --- a/Application/src/main/java/com/example/android/common/logger/LogNode.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.android.common.logger; - -/** - * Basic interface for a logging system that can output to one or more targets. - * Note that in addition to classes that will output these logs in some format, - * one can also implement this interface over a filter and insert that in the chain, - * such that no targets further down see certain data, or see manipulated forms of the data. - * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data - * it received to HTML and sent it along to the next node in the chain, without printing it - * anywhere. - */ -public interface LogNode { - - /** - * Instructs first LogNode in the list to print the log data provided. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public void println(int priority, String tag, String msg, Throwable tr); - -} diff --git a/Application/src/main/java/com/example/android/common/logger/LogView.java b/Application/src/main/java/com/example/android/common/logger/LogView.java deleted file mode 100644 index c01542b..0000000 --- a/Application/src/main/java/com/example/android/common/logger/LogView.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.android.common.logger; - -import android.app.Activity; -import android.content.Context; -import android.util.*; -import android.widget.TextView; - -/** Simple TextView which is used to output log data received through the LogNode interface. -*/ -public class LogView extends TextView implements LogNode { - - public LogView(Context context) { - super(context); - } - - public LogView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LogView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Formats the log data and prints it out to the LogView. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - - - String priorityStr = null; - - // For the purposes of this View, we want to print the priority as readable text. - switch(priority) { - case android.util.Log.VERBOSE: - priorityStr = "VERBOSE"; - break; - case android.util.Log.DEBUG: - priorityStr = "DEBUG"; - break; - case android.util.Log.INFO: - priorityStr = "INFO"; - break; - case android.util.Log.WARN: - priorityStr = "WARN"; - break; - case android.util.Log.ERROR: - priorityStr = "ERROR"; - break; - case android.util.Log.ASSERT: - priorityStr = "ASSERT"; - break; - default: - break; - } - - // Handily, the Log class has a facility for converting a stack trace into a usable string. - String exceptionStr = null; - if (tr != null) { - exceptionStr = android.util.Log.getStackTraceString(tr); - } - - // Take the priority, tag, message, and exception, and concatenate as necessary - // into one usable line of text. - final StringBuilder outputBuilder = new StringBuilder(); - - String delimiter = "\t"; - appendIfNotNull(outputBuilder, priorityStr, delimiter); - appendIfNotNull(outputBuilder, tag, delimiter); - appendIfNotNull(outputBuilder, msg, delimiter); - appendIfNotNull(outputBuilder, exceptionStr, delimiter); - - // In case this was originally called from an AsyncTask or some other off-UI thread, - // make sure the update occurs within the UI thread. - ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { - @Override - public void run() { - // Display the text we just generated within the LogView. - appendToLog(outputBuilder.toString()); - } - }))); - - if (mNext != null) { - mNext.println(priority, tag, msg, tr); - } - } - - public LogNode getNext() { - return mNext; - } - - public void setNext(LogNode node) { - mNext = node; - } - - /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since - * the logger takes so many arguments that might be null, this method helps cut out some of the - * agonizing tedium of writing the same 3 lines over and over. - * @param source StringBuilder containing the text to append to. - * @param addStr The String to append - * @param delimiter The String to separate the source and appended strings. A tab or comma, - * for instance. - * @return The fully concatenated String as a StringBuilder - */ - private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { - if (addStr != null) { - if (addStr.length() == 0) { - delimiter = ""; - } - - return source.append(addStr).append(delimiter); - } - return source; - } - - // The next LogNode in the chain. - LogNode mNext; - - /** Outputs the string as a new line of log data in the LogView. */ - public void appendToLog(String s) { - append("\n" + s); - } - - -} diff --git a/Application/src/main/java/com/example/android/common/logger/LogWrapper.java b/Application/src/main/java/com/example/android/common/logger/LogWrapper.java deleted file mode 100644 index 16a9e7b..0000000 --- a/Application/src/main/java/com/example/android/common/logger/LogWrapper.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.android.common.logger; - -import android.util.Log; - -/** - * Helper class which wraps Android's native Log utility in the Logger interface. This way - * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. - */ -public class LogWrapper implements LogNode { - - // For piping: The next node to receive Log data after this one has done its work. - private LogNode mNext; - - /** - * Returns the next LogNode in the linked list. - */ - public LogNode getNext() { - return mNext; - } - - /** - * Sets the LogNode data will be sent to.. - */ - public void setNext(LogNode node) { - mNext = node; - } - - /** - * Prints data out to the console using Android's native log mechanism. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - // There actually are log methods that don't take a msg parameter. For now, - // if that's the case, just convert null to the empty string and move on. - String useMsg = msg; - if (useMsg == null) { - useMsg = ""; - } - - // If an exeption was provided, convert that exception to a usable string and attach - // it to the end of the msg method. - if (tr != null) { - msg += "\n" + Log.getStackTraceString(tr); - } - - // This is functionally identical to Log.x(tag, useMsg); - // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) - Log.println(priority, tag, useMsg); - - // If this isn't the last node in the chain, move things along. - if (mNext != null) { - mNext.println(priority, tag, msg, tr); - } - } -} diff --git a/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java b/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java deleted file mode 100644 index 19967dc..0000000 --- a/Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.android.common.logger; - -/** - * Simple {@link LogNode} filter, removes everything except the message. - * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, - * just easy-to-read message updates as they're happening. - */ -public class MessageOnlyLogFilter implements LogNode { - - LogNode mNext; - - /** - * Takes the "next" LogNode as a parameter, to simplify chaining. - * - * @param next The next LogNode in the pipeline. - */ - public MessageOnlyLogFilter(LogNode next) { - mNext = next; - } - - public MessageOnlyLogFilter() { - } - - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - if (mNext != null) { - getNext().println(Log.NONE, null, msg, null); - } - } - - /** - * Returns the next LogNode in the chain. - */ - public LogNode getNext() { - return mNext; - } - - /** - * Sets the LogNode data will be sent to.. - */ - public void setNext(LogNode node) { - mNext = node; - } - -} diff --git a/Application/src/main/java/com/example/android/networkconnect/DownloadCallback.java b/Application/src/main/java/com/example/android/networkconnect/DownloadCallback.java new file mode 100644 index 0000000..b553e85 --- /dev/null +++ b/Application/src/main/java/com/example/android/networkconnect/DownloadCallback.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.networkconnect; + +import android.net.NetworkInfo; +import android.support.annotation.IntDef; + +/** + * Sample interface containing bare minimum methods needed for an asynchronous task + * to update the UI Context. + */ + +public interface DownloadCallback { + interface Progress { + int ERROR = -1; + int CONNECT_SUCCESS = 0; + int GET_INPUT_STREAM_SUCCESS = 1; + int PROCESS_INPUT_STREAM_IN_PROGRESS = 2; + int PROCESS_INPUT_STREAM_SUCCESS = 3; + } + + /** + * Indicates that the callback handler needs to update its appearance or information based on + * the result of the task. Expected to be called from the main thread. + */ + void updateFromDownload(String result); + + /** + * Get the device's active network status in the form of a NetworkInfo object. + */ + NetworkInfo getActiveNetworkInfo(); + + /** + * Indicate to callback handler any progress update. + * @param progressCode must be one of the constants defined in DownloadCallback.Progress. + * @param percentComplete must be 0-100. + */ + void onProgressUpdate(int progressCode, int percentComplete); + + /** + * Indicates that the download operation has finished. This method is called even if the + * download hasn't completed successfully. + */ + void finishDownloading(); + +} diff --git a/Application/src/main/java/com/example/android/networkconnect/MainActivity.java b/Application/src/main/java/com/example/android/networkconnect/MainActivity.java index 3ad4646..d490f5d 100755 --- a/Application/src/main/java/com/example/android/networkconnect/MainActivity.java +++ b/Application/src/main/java/com/example/android/networkconnect/MainActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,55 +16,42 @@ package com.example.android.networkconnect; -import android.os.AsyncTask; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Bundle; import android.support.v4.app.FragmentActivity; -import android.util.TypedValue; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; - -import com.example.android.common.logger.Log; -import com.example.android.common.logger.LogFragment; -import com.example.android.common.logger.LogWrapper; -import com.example.android.common.logger.MessageOnlyLogFilter; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; +import android.widget.TextView; /** - * Sample application demonstrating how to connect to the network and fetch raw - * HTML. It uses AsyncTask to do the fetch on a background thread. To establish - * the network connection, it uses HttpURLConnection. + * Sample Activity demonstrating how to connect to the network and fetch raw + * HTML. It uses a Fragment that encapsulates the network operations on an AsyncTask. * - * This sample uses the logging framework to display log output in the log - * fragment (LogFragment). + * This sample uses a TextView to display output. */ -public class MainActivity extends FragmentActivity { - - public static final String TAG = "Network Connect"; +public class MainActivity extends FragmentActivity implements DownloadCallback { - // Reference to the fragment showing events, so we can clear it with a button + // Reference to the TextView showing fetched data, so we can clear it with a button // as necessary. - private LogFragment mLogFragment; + private TextView mDataText; + + // Keep a reference to the NetworkFragment which owns the AsyncTask object + // that is used to execute network ops. + private NetworkFragment mNetworkFragment; + + // Boolean telling us whether a download is in progress, so we don't trigger overlapping + // downloads with consecutive button clicks. + private boolean mDownloading = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sample_main); - - // Initialize text fragment that displays intro text. - SimpleTextFragment introFragment = (SimpleTextFragment) - getSupportFragmentManager().findFragmentById(R.id.intro_fragment); - introFragment.setText(R.string.welcome_message); - introFragment.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16.0f); - - // Initialize the logging framework. - initializeLogging(); + mDataText = (TextView) findViewById(R.id.data_text); + mNetworkFragment = NetworkFragment.getInstance(getSupportFragmentManager(), "https://www.google.com"); } @Override @@ -79,111 +66,65 @@ public boolean onOptionsItemSelected(MenuItem item) { // When the user clicks FETCH, fetch the first 500 characters of // raw HTML from www.google.com. case R.id.fetch_action: - new DownloadTask().execute("http://www.google.com"); + startDownload(); return true; - // Clear the log view fragment. + // Clear the text and cancel download. case R.id.clear_action: - mLogFragment.getLogView().setText(""); - return true; + finishDownloading(); + mDataText.setText(""); + return true; } return false; } - /** - * Implementation of AsyncTask, to fetch the data in the background away from - * the UI thread. - */ - private class DownloadTask extends AsyncTask { - - @Override - protected String doInBackground(String... urls) { - try { - return loadFromNetwork(urls[0]); - } catch (IOException e) { - return getString(R.string.connection_error); - } - } - - /** - * Uses the logging framework to display the output of the fetch - * operation in the log fragment. - */ - @Override - protected void onPostExecute(String result) { - Log.i(TAG, result); + private void startDownload() { + if (!mDownloading && mNetworkFragment != null) { + // Execute the async download. + mNetworkFragment.startDownload(); + mDownloading = true; } } - /** Initiates the fetch operation. */ - private String loadFromNetwork(String urlString) throws IOException { - InputStream stream = null; - String str =""; - - try { - stream = downloadUrl(urlString); - str = readIt(stream, 500); - } finally { - if (stream != null) { - stream.close(); - } + @Override + public void updateFromDownload(String result) { + if (result != null) { + mDataText.setText(result); + } else { + mDataText.setText(getString(R.string.connection_error)); } - return str; } - /** - * Given a string representation of a URL, sets up a connection and gets - * an input stream. - * @param urlString A string representation of a URL. - * @return An InputStream retrieved from a successful HttpURLConnection. - * @throws java.io.IOException - */ - private InputStream downloadUrl(String urlString) throws IOException { - // BEGIN_INCLUDE(get_inputstream) - URL url = new URL(urlString); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setReadTimeout(10000 /* milliseconds */); - conn.setConnectTimeout(15000 /* milliseconds */); - conn.setRequestMethod("GET"); - conn.setDoInput(true); - // Start the query - conn.connect(); - InputStream stream = conn.getInputStream(); - return stream; - // END_INCLUDE(get_inputstream) + @Override + public NetworkInfo getActiveNetworkInfo() { + ConnectivityManager connectivityManager = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo; } - /** Reads an InputStream and converts it to a String. - * @param stream InputStream containing HTML from targeted site. - * @param len Length of string that this method returns. - * @return String concatenated according to len parameter. - * @throws java.io.IOException - * @throws java.io.UnsupportedEncodingException - */ - private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException { - Reader reader = null; - reader = new InputStreamReader(stream, "UTF-8"); - char[] buffer = new char[len]; - reader.read(buffer); - return new String(buffer); + @Override + public void finishDownloading() { + mDownloading = false; + if (mNetworkFragment != null) { + mNetworkFragment.cancelDownload(); + } } - /** Create a chain of targets that will receive log data */ - public void initializeLogging() { - - // Using Log, front-end to the logging chain, emulates - // android.util.log method signatures. - - // Wraps Android's native log framework - LogWrapper logWrapper = new LogWrapper(); - Log.setLogNode(logWrapper); - - // A filter that strips out everything except the message text. - MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); - logWrapper.setNext(msgFilter); - - // On screen logging via a fragment with a TextView. - mLogFragment = - (LogFragment) getSupportFragmentManager().findFragmentById(R.id.log_fragment); - msgFilter.setNext(mLogFragment.getLogView()); + @Override + public void onProgressUpdate(int progressCode, int percentComplete) { + switch(progressCode) { + // You can add UI behavior for progress updates here. + case Progress.ERROR: + break; + case Progress.CONNECT_SUCCESS: + break; + case Progress.GET_INPUT_STREAM_SUCCESS: + break; + case Progress.PROCESS_INPUT_STREAM_IN_PROGRESS: + mDataText.setText("" + percentComplete + "%"); + break; + case Progress.PROCESS_INPUT_STREAM_SUCCESS: + break; + } } } diff --git a/Application/src/main/java/com/example/android/networkconnect/NetworkFragment.java b/Application/src/main/java/com/example/android/networkconnect/NetworkFragment.java new file mode 100644 index 0000000..cc41075 --- /dev/null +++ b/Application/src/main/java/com/example/android/networkconnect/NetworkFragment.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.networkconnect; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Implementation of headless Fragment that runs an AsyncTask to fetch data from the network. + */ +public class NetworkFragment extends Fragment { + public static final String TAG = "NetworkFragment"; + + private static final String URL_KEY = "UrlKey"; + + private DownloadCallback mCallback; + private DownloadTask mDownloadTask; + private String mUrlString; + + /** + * Static initializer for NetworkFragment that sets the URL of the host it will be downloading + * from. + */ + public static NetworkFragment getInstance(FragmentManager fragmentManager, String url) { + // Recover NetworkFragment in case we are re-creating the Activity due to a config change. + // This is necessary because NetworkFragment might have a task that began running before + // the config change and has not finished yet. + // The NetworkFragment is recoverable via this method because it calls + // setRetainInstance(true) upon creation. + NetworkFragment networkFragment = (NetworkFragment) fragmentManager + .findFragmentByTag(NetworkFragment.TAG); + if (networkFragment == null) { + networkFragment = new NetworkFragment(); + Bundle args = new Bundle(); + args.putString(URL_KEY, url); + networkFragment.setArguments(args); + fragmentManager.beginTransaction().add(networkFragment, TAG).commit(); + } + return networkFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Retain this Fragment across configuration changes in the host Activity. + setRetainInstance(true); + mUrlString = getArguments().getString(URL_KEY); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + // Host Activity will handle callbacks from task. + mCallback = (DownloadCallback)context; + } + + @Override + public void onDetach() { + super.onDetach(); + // Clear reference to host Activity. + mCallback = null; + } + + @Override + public void onDestroy() { + // Cancel task when Fragment is destroyed. + cancelDownload(); + super.onDestroy(); + } + + /** + * Start non-blocking execution of DownloadTask. + */ + public void startDownload() { + cancelDownload(); + mDownloadTask = new DownloadTask(); + mDownloadTask.execute(mUrlString); + } + + /** + * Cancel (and interrupt if necessary) any ongoing DownloadTask execution. + */ + public void cancelDownload() { + if (mDownloadTask != null) { + mDownloadTask.cancel(true); + mDownloadTask = null; + } + } + + /** + * Implementation of AsyncTask that runs a network operation on a background thread. + */ + private class DownloadTask extends AsyncTask { + + /** + * Wrapper class that serves as a union of a result value and an exception. When the + * download task has completed, either the result value or exception can be a non-null + * value. This allows you to pass exceptions to the UI thread that were thrown during + * doInBackground(). + */ + class Result { + public String mResultValue; + public Exception mException; + public Result(String resultValue) { + mResultValue = resultValue; + } + public Result(Exception exception) { + mException = exception; + } + } + + /** + * Cancel background network operation if we do not have network connectivity. + */ + @Override + protected void onPreExecute() { + if (mCallback != null) { + NetworkInfo networkInfo = mCallback.getActiveNetworkInfo(); + if (networkInfo == null || !networkInfo.isConnected() || + (networkInfo.getType() != ConnectivityManager.TYPE_WIFI + && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) { + // If no connectivity, cancel task and update Callback with null data. + mCallback.updateFromDownload(null); + cancel(true); + } + } + } + + /** + * Defines work to perform on the background thread. + */ + @Override + protected Result doInBackground(String... urls) { + Result result = null; + if (!isCancelled() && urls != null && urls.length > 0) { + String urlString = urls[0]; + try { + URL url = new URL(urlString); + String resultString = downloadUrl(url); + if (resultString != null) { + result = new Result(resultString); + } else { + throw new IOException("No response received."); + } + } catch(Exception e) { + result = new Result(e); + } + } + return result; + } + + /** + * Send DownloadCallback a progress update. + */ + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + if (values.length >= 2) { + mCallback.onProgressUpdate(values[0], values[1]); + } + } + + /** + * Updates the DownloadCallback with the result. + */ + @Override + protected void onPostExecute(Result result) { + if (result != null && mCallback != null) { + if (result.mException != null) { + mCallback.updateFromDownload(result.mException.getMessage()); + } else if (result.mResultValue != null) { + mCallback.updateFromDownload(result.mResultValue); + } + mCallback.finishDownloading(); + } + } + + /** + * Override to add special behavior for cancelled AsyncTask. + */ + @Override + protected void onCancelled(Result result) { + } + + /** + * Given a URL, sets up a connection and gets the HTTP response body from the server. + * If the network request is successful, it returns the response body in String form. Otherwise, + * it will throw an IOException. + */ + private String downloadUrl(URL url) throws IOException { + InputStream stream = null; + HttpsURLConnection connection = null; + String result = null; + try { + connection = (HttpsURLConnection) url.openConnection(); + // Timeout for reading InputStream arbitrarily set to 3000ms. + connection.setReadTimeout(3000); + // Timeout for connection.connect() arbitrarily set to 3000ms. + connection.setConnectTimeout(3000); + // For this use case, set HTTP method to GET. + connection.setRequestMethod("GET"); + // Already true by default but setting just in case; needs to be true since this request + // is carrying an input (response) body. + connection.setDoInput(true); + // Open communications link (network traffic occurs here). + connection.connect(); + publishProgress(DownloadCallback.Progress.CONNECT_SUCCESS); + int responseCode = connection.getResponseCode(); + if (responseCode != HttpsURLConnection.HTTP_OK) { + throw new IOException("HTTP error code: " + responseCode); + } + // Retrieve the response body as an InputStream. + stream = connection.getInputStream(); + publishProgress(DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS, 0); + if (stream != null) { + // Converts Stream to String with max length of 500. + result = readStream(stream, 500); + publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_SUCCESS, 0); + } + } finally { + // Close Stream and disconnect HTTPS connection. + if (stream != null) { + stream.close(); + } + if (connection != null) { + connection.disconnect(); + } + } + return result; + } + + /** + * Converts the contents of an InputStream to a String. + */ + private String readStream(InputStream stream, int maxLength) throws IOException { + String result = null; + // Read InputStream using the UTF-8 charset. + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + // Create temporary buffer to hold Stream data with specified max length. + char[] buffer = new char[maxLength]; + // Populate temporary buffer with Stream data. + int numChars = 0; + int readSize = 0; + while (numChars < maxLength && readSize != -1) { + numChars += readSize; + int pct = (100 * numChars) / maxLength; + publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_IN_PROGRESS, pct); + readSize = reader.read(buffer, numChars, buffer.length - numChars); + } + if (numChars != -1) { + // The stream was not empty. + // Create String that is actual length of response body if actual length was less than + // max length. + numChars = Math.min(numChars, maxLength); + result = new String(buffer, 0, numChars); + } + return result; + } + } +} diff --git a/Application/src/main/java/com/example/android/networkconnect/SimpleTextFragment.java b/Application/src/main/java/com/example/android/networkconnect/SimpleTextFragment.java deleted file mode 100644 index 3202937..0000000 --- a/Application/src/main/java/com/example/android/networkconnect/SimpleTextFragment.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.android.networkconnect; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -/** - * Simple fragment containing only a TextView. Used by TextPagerAdapter to create - * tutorial-style pages for apps. - */ -public class SimpleTextFragment extends Fragment { - - // Contains the text that will be displayed by this Fragment - String mText; - - // Contains a resource ID for the text that will be displayed by this fragment. - int mTextId = -1; - - // Keys which will be used to store/retrieve text passed in via setArguments. - public static final String TEXT_KEY = "text"; - public static final String TEXT_ID_KEY = "text_id"; - - // For situations where the app wants to modify text at Runtime, exposing the TextView. - private TextView mTextView; - - public SimpleTextFragment() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Before initializing the textView, check if any arguments were provided via setArguments. - processArguments(); - - // Create a new TextView and set its text to whatever was provided. - mTextView = new TextView(getActivity()); - mTextView.setGravity(Gravity.CENTER); - - if (mText != null) { - mTextView.setText(mText); - Log.i("SimpleTextFragment", mText); - } - return mTextView; - } - - public TextView getTextView() { - return mTextView; - } - - /** - * Changes the text for this TextView, according to the resource ID provided. - * @param stringId A resource ID representing the text content for this Fragment's TextView. - */ - public void setText(int stringId) { - getTextView().setText(getActivity().getString(stringId)); - } - - /** - * Processes the arguments passed into this Fragment via setArguments method. - * Currently the method only looks for text or a textID, nothing else. - */ - public void processArguments() { - // For most objects we'd handle the multiple possibilities for initialization variables - // as multiple constructors. For Fragments, however, it's customary to use - // setArguments / getArguments. - if (getArguments() != null) { - Bundle args = getArguments(); - if (args.containsKey(TEXT_KEY)) { - mText = args.getString(TEXT_KEY); - Log.d("Constructor", "Added Text."); - } else if (args.containsKey(TEXT_ID_KEY)) { - mTextId = args.getInt(TEXT_ID_KEY); - mText = getString(mTextId); - } - } - } -} \ No newline at end of file diff --git a/Application/src/main/res/layout/sample_main.xml b/Application/src/main/res/layout/sample_main.xml index 76fa7d7..00e219c 100755 --- a/Application/src/main/res/layout/sample_main.xml +++ b/Application/src/main/res/layout/sample_main.xml @@ -21,20 +21,22 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> - + android:layout_height="match_parent" + android:textSize="16sp" + android:text="@string/welcome_message" + android:freezesText="true"/> - + android:layout_height="match_parent" + android:freezesText="true"/> diff --git a/Application/src/main/res/values/base-strings.xml b/Application/src/main/res/values/base-strings.xml index ae4485e..820b921 100644 --- a/Application/src/main/res/values/base-strings.xml +++ b/Application/src/main/res/values/base-strings.xml @@ -22,7 +22,7 @@ This sample demonstrates how to connect to the network and fetch raw HTML using - HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. + HttpsURLConnection. AsyncTask is used to perform the fetch on a background thread. ]]> diff --git a/Application/src/main/res/values/strings.xml b/Application/src/main/res/values/strings.xml index 1e7915a..d2be145 100755 --- a/Application/src/main/res/values/strings.xml +++ b/Application/src/main/res/values/strings.xml @@ -18,8 +18,8 @@ Welcome to Network Connect! Click FETCH to fetch the first 500 characters of raw HTML from www.google.com. - Fetch Clear Connection error. + Fetch cancelled. diff --git a/Application/src/main/res/xml/network_security_config.xml b/Application/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..4631b89 --- /dev/null +++ b/Application/src/main/res/xml/network_security_config.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 2a16753..8c9ea50 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,30 @@ Android NetworkConnect Sample =================================== This sample demonstrates how to connect to the network and fetch raw HTML using -HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. +HttpsURLConnection. AsyncTask is used to perform the fetch on a background thread. Introduction ------------ -This sample application demonstrates how to connect to the network and fetch raw -HTML. It uses AsyncTask, a background task manager, to perform the network fetch -on a background thread. By using AsyncTask, the app can avoid hanging on the UI -thread, and instead update the UI when the response is ready. To establish the -network connection, the sample uses HttpURLConnection. +This sample demonstrates how to connect to the network and fetch raw HTML using +[`HttpsURLConnection`][4]. Since API 11, it is required by default that all network +operations run on a background thread in order to avoid hanging on the UI thread. Only +when the network response is ready should the work return to the main thread to update +the UI. An [`AsyncTask`][3] is a viable background task manager that is used to perform +the network operation and return to the UI thread upon completion. + +The sample also utilizes the [`ConnectivityManager`][1] to determine if you have +a network connection, and if so, what type of connection it is. + +Using an [`AsyncTaskLoader`][6] or an [`IntentService`][5] are two common alternatives +for managing longer running background work. + +[1]: https://developer.android.com/reference/android/net/ConnectivityManager.html +[2]: https://developer.android.com/reference/android/net/NetworkInfo.html +[3]: https://developer.android.com/reference/android/os/AsyncTask.html +[4]: https://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html +[5]: https://developer.android.com/reference/android/app/IntentService.html +[6]: https://developer.android.com/reference/android/content/AsyncTaskLoader.html Pre-requisites --------------