Merge "Merge cronet sample app into Development app"

This commit is contained in:
Patrick Rohr
2023-06-28 16:43:50 +00:00
committed by Gerrit Code Review
6 changed files with 344 additions and 0 deletions

View File

@@ -112,6 +112,12 @@
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="HttpEngineActivity" android:label="Http Client" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
<activity android:name="InstrumentationList" android:label="Instrumentation"
android:exported="true">

View File

@@ -0,0 +1,39 @@
<!-- Copyright (C) 2023 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".CronetSampleActivity">
<TextView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/resultView">
<TextView
android:id="@+id/dataView"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"/>
</ScrollView>
</RelativeLayout>

View File

@@ -0,0 +1,56 @@
<!-- Copyright (C) 2023 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/urlView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginStart="4dp"
android:textSize="16sp"
android:text="@string/urlText" />
<!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
<EditText
tools:ignore="LabelFor"
android:id="@+id/urlText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri" />
<TextView
android:id="@+id/postView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
android:layout_marginStart="4dp"
android:textSize="16sp"
android:text="@string/postText" />
<!-- TODO(crbug.com/900912): Fix and remove lint ignore -->
<EditText
tools:ignore="LabelFor"
android:id="@+id/postText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -225,4 +225,8 @@
<string name="scancard">Scan SD card</string>
<string name="numsongs"># of albums</string>
<string name="insertbutton">Insert %1s albums</string>
<!-- HttpEngineActivity -->
<string name="urlText">Enter a URL</string>
<string name="postText">Enter post data (leave it blank for GET request)</string>
</resources>

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2023 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.android.development;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.http.HttpEngine;
import android.net.http.HttpException;
import android.net.http.UploadDataProvider;
import android.net.http.UploadDataSink;
import android.net.http.UrlRequest;
import android.net.http.UrlResponseInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Activity for managing HttpEngine interactions.
*/
public class HttpEngineActivity extends Activity {
private static final String TAG = HttpEngineActivity.class.getSimpleName();
private HttpEngine mHttpEngine;
private String mUrl;
private TextView mResultText;
private TextView mReceiveDataText;
class SimpleUrlRequestCallback implements UrlRequest.Callback {
private ByteArrayOutputStream mBytesReceived = new ByteArrayOutputStream();
private WritableByteChannel mReceiveChannel = Channels.newChannel(mBytesReceived);
@Override
public void onRedirectReceived(
UrlRequest request, UrlResponseInfo info, String newLocationUrl) {
Log.i(TAG, "****** onRedirectReceived ******");
request.followRedirect();
}
@Override
public void onResponseStarted(UrlRequest request, UrlResponseInfo info) {
Log.i(TAG, "****** Response Started ******");
Log.i(TAG, "*** Headers Are *** " + info.getHeaders());
request.read(ByteBuffer.allocateDirect(32 * 1024));
}
@Override
public void onReadCompleted(
UrlRequest request, UrlResponseInfo info, ByteBuffer byteBuffer) {
byteBuffer.flip();
Log.i(TAG, "****** onReadCompleted ******" + byteBuffer);
try {
mReceiveChannel.write(byteBuffer);
} catch (IOException e) {
Log.i(TAG, "IOException during ByteBuffer read. Details: ", e);
}
byteBuffer.clear();
request.read(byteBuffer);
}
@Override
public void onSucceeded(UrlRequest request, UrlResponseInfo info) {
Log.i(TAG, "****** Request Completed, status code is " + info.getHttpStatusCode()
+ ", total received bytes is " + info.getReceivedByteCount());
final String receivedData = mBytesReceived.toString();
final String url = info.getUrl();
final String text = "Completed " + url + " (" + info.getHttpStatusCode() + ")";
HttpEngineActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
mResultText.setText(text);
mReceiveDataText.setText(receivedData);
promptForURL(url);
}
});
}
@Override
public void onFailed(UrlRequest request, UrlResponseInfo info, HttpException error) {
Log.i(TAG, "****** onFailed, error is: " + error.getMessage());
final String url = mUrl;
final String text = "Failed " + mUrl + " (" + error.getMessage() + ")";
HttpEngineActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
mResultText.setText(text);
promptForURL(url);
}
});
}
@Override
public void onCanceled(UrlRequest request, UrlResponseInfo info) {
Log.i(TAG, "****** onCanceled ******");
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.http_engine_activity);
mResultText = (TextView) findViewById(R.id.resultView);
mReceiveDataText = (TextView) findViewById(R.id.dataView);
mReceiveDataText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
promptForURL(mUrl);
}
});
HttpEngine.Builder myBuilder = new HttpEngine.Builder(this);
myBuilder.setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024)
.setEnableHttp2(true)
.setEnableQuic(true);
mHttpEngine = myBuilder.build();
String appUrl = (getIntent() != null ? getIntent().getDataString() : null);
if (appUrl == null) {
promptForURL("https://");
} else {
startWithURL(appUrl);
}
}
private void promptForURL(String url) {
Log.i(TAG, "No URL provided via intent, prompting user...");
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Enter a URL");
LayoutInflater inflater = getLayoutInflater();
View alertView = inflater.inflate(R.layout.http_engine_dialog, null);
final EditText urlInput = (EditText) alertView.findViewById(R.id.urlText);
urlInput.setText(url);
final EditText postInput = (EditText) alertView.findViewById(R.id.postText);
alert.setView(alertView);
alert.setPositiveButton("Load", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int button) {
String url = urlInput.getText().toString();
String postData = postInput.getText().toString();
startWithURL(url, postData);
}
});
alert.show();
}
private void applyPostDataToUrlRequestBuilder(
UrlRequest.Builder builder, Executor executor, String postData) {
if (postData != null && postData.length() > 0) {
builder.setHttpMethod("POST");
builder.addHeader("Content-Type", "application/x-www-form-urlencoded");
// TODO: make android.net.http.apihelpers.UploadDataProviders accessible.
builder.setUploadDataProvider(new UploadDataProvider() {
@Override
public long getLength() {
return postData.length();
}
@Override
public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
byteBuffer.put(postData.getBytes());
uploadDataSink.onReadSucceeded(/*finalChunk*/ false);
}
@Override
public void rewind(UploadDataSink uploadDataSink) {
// noop
uploadDataSink.onRewindSucceeded();
}
}, executor);
}
}
private void startWithURL(String url) {
startWithURL(url, null);
}
private void startWithURL(String url, String postData) {
Log.i(TAG, "UrlRequest started: " + url);
mUrl = url;
Executor executor = Executors.newSingleThreadExecutor();
UrlRequest.Callback callback = new SimpleUrlRequestCallback();
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(url, executor, callback);
applyPostDataToUrlRequestBuilder(builder, executor, postData);
builder.build().start();
}
}