I am learning to work with google cloud endpoint. I am trying to connect from my device however, I keep getting an error.
failed to connect to /192.168.1.100 (port 8080) after 20000ms: isConnected failed: ECONNREFUSED (Connection refused)
How do I overcome this issue, I would like to run on a real device?
Get Jokes.class
public class GetJokes extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
if (myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
Log.i("Failed to con", result);
}
}
MainActivityFragment.class
public class MainActivityFragment extends Fragment {
public static final String JOKE = "JOKE";
public MainActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container, false);
Button tellJokeButton = (Button)root.findViewById(R.id.tellJoke_button);
tellJokeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new GetJokes().execute(new Pair<Context, String>(getActivity(), "Manfred"));
}
});
AdView mAdView = (AdView) root.findViewById(R.id.adView);
// Create an ad request. Check logcat output for the hashed device ID to
// get test ads on a physical device. e.g.
// "Use AdRequest.Builder.addTestDevice("ABCDEF012345") to get test ads on this device."
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build();
mAdView.loadAd(adRequest);
return root;
}
public void tellJoke(){
Intent intent = new Intent(getActivity(), JokeTellerActivity.class);
//TODO: get jokes from Google Cloud endpoint
intent.putExtra(JOKE,
getActivity().getString(R.string.joke)
);
startActivity(intent);
}
}
MyEndPoint.class
public class MyEndpoint {
public static List<Joke> mJokes = new ArrayList<>();
/** A simple endpoint method that takes a name and says Hi back */
#ApiMethod(name = "sayHi")
public MyBean sayHi(#Named("name") String name) {
MyBean response = new MyBean();
response.setData("Hi, " + name);
return response;
}
}
I've tried running it on several different devices and none of them would connect.
I've had the same problem because I'm also doing this project from the Android Nanodegree (Udacity).
I've found the solution in this question. Because the answers there are a bit messy or uncomplete, I'm going to explain in detail what I did to run the local server on my computer and test it with a real device.
1) Add httpAddress = "0.0.0.0" to the appengine block in your build.gradle file of the backend (Google Cloud Endpoint) module, like this:
appengine {
...
httpAddress = "0.0.0.0"
}
According to a comment on the linked question this means that 'will accept from anywhere'.
2) Run your backend module locally (ie. start the web server). This is trivial: select the backend configuration on the drop-down menu and press the green button. You can find more detailed instructions here.
You should now be able to open a browser (on your computer) and navigate to http://localhost:8080/ and see a webpage with 'Hello, Endpoints!' and many other stuff.
3) Find your computer's IP address (on a Mac go to System Preferences -> Network) and then set it as the root url in your Android app code, like this:
MyApi.Builder builder = new MyApi.Builder(
AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(),
null
).setRootUrl("http://192.168.1.36:8080/_ah/api/")
That's all!
So you've setup an AppEngine based cloud endpoint as a local development server on the same machine in which you are running an Android emulator? Be sure you're using the correct IP address (and port) for the endpoint. The local IP address you need is not the one in use by the Android emulator, but the host dev machine. For example:
Local Dev Machine: 192.168.1.100
AppEngine server on dev machine: listening on 8080
Android Emulator running on dev machine: 10.0.2.2
You should be able to access the API explorer on the dev machine via a browser on the Android emulator as well. You can use that to sanity check that your app is using the correct address:
http://192.168.1.100:8080/_ah/api/explorer
Related
I'm working on a Blazor Hybrid App and currently trying to access a .NET Web API from my phone.
I have deployed a .NET Web Application to IIS. API returns just a WeatherForecast data (for those who're not familiar, data type is already defined in project and comes with the template) in JSON.
API Response is something like this:
[
{"date":"2020-09-18T15:55:27.4724752+03:00","temperatureC":-6,"temperatureF":22,"summary":"Hot"},
{"date":"2020-09-19T15:55:27.4725087+03:00","temperatureC":27,"temperatureF":80,"summary":"Bracing"},
{"date":"2020-09-20T15:55:27.4725155+03:00","temperatureC":54,"temperatureF":129,"summary":"Bracing"},
{"date":"2020-09-21T15:55:27.4725221+03:00","temperatureC":1,"temperatureF":33,"summary":"Scorching"},
{"date":"2020-09-22T15:55:27.4725302+03:00","temperatureC":-3,"temperatureF":27,"summary":"Chilly"}
]
I deployed it to my localhost at port 3004. So both in my PC's browser and my mobile phone's browser I can successfully reach to this address and get this response.
However in my mobile application there is a method responsible for retrieving the data, defined as:
AppState.cs:
public async Task<List<WeatherForecast>> GetForecastAsync()
{
return await _http.GetFromJsonAsync<List<WeatherForecast>>("http://192.168.1.22:3004/weatherforecast");
}
and this is called from Index.razor:
#inject AppState appState
#if(todos == null){
<p> Loading </p>
}
else {
// loop todos in foreach
}
#code {
List<Todo> todos;
protected override async Task OnInitializedAsync()
{
todos = await appState.GetForecastAsync();
appState.OnChange += UpdateState;
}
This GET Request returns null. I've tried it with JSON placeholder from https://jsonplaceholder.typicode.com/todos/ (Changed the WeatherForecast to Todo of course) there was no problem!
My Attemps
For possible solutions I've tried to
change my local IP to 10.0.2.2:3004 since I'm on the android phone but no use.
I've tried with both http:// and https:// but still no use.
Configure CORS to allow any origins in the API :
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddDefaultPolicy(builder => builder.AllowAnyOrigin()));
services.AddControllers();
}
//...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
//...
}
How can I reach the API from the mobile app?
After few hours of researching and trial and error I found it.
So I'll explain the flow step by step:
First when the page we want opens, we want to fetch the data from a remote database. In my case I call it from Index.razor, my home page:
Index.razor
#page "/"
#inject AppState appState
#implements IDisposable
#if (forecasts== null)
{
<p>Null</p>
}
else
{
#foreach (var forecast in forecasts)
{
<p>#forecast .Date</p>
<p>#forecast .TemperatureC</p>
<p>#forecast .TemperatureF</p>
<p>#forecast .Summary</p>
}
}
#code {
List<WeatherForecast> forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await appState.GetForecastAsync(); // We call the Get Forecast Async via appState
appState.OnChange += UpdateState;
}
public void Dispose() => appState.OnChange -= UpdateState;
private async Task UpdateState()
{
StateHasChanged();
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
We need the AppState because we need to keep track of state, so it's like a state manager.
After that we jump to AppState.cs:
class AppState
{
HttpClient _http;
public AppState()
{
_http = new HttpClient() { BaseAddress = new Uri("http://192.168.1.22:3000/") };
public async Task<List<WeatherForecast>> GetForecastAsync()
{
try
{
return await _http.GetFromJsonAsync<List<WeatherForecast>>("weatherforecast");
}
catch (Exception ex)
{
Debug.WriteLine(#"\tERROR {0}", ex.Message);
}
return new List<WeatherForecast>();
}
//There is also OnChange method here but it's irrelevant.
}
I've downloaded ISSexpress-proxy and proxied the api to another port because I was having "Invalid hostname" error.
When initializing HttpClient I gave the baseAddress as my local IP, not 10.0.2.2 where you use to access to localhost on Android.
I was still getting errors on HTTP communication. In AndroidManifest.xml file I added android:targetSandboxVersion="1" to <manifest> and android:usesCleartextTraffic="true" to <application>. Thanks to this answer I found how to solve it.
Final Result
I have android native application & web service. Our application is University application. Generate long live token for session issue.So we planned to go for MDM, i searched on Google, , I got some solution
Open Mobster (Open Source Mobile Enterprise Backend)
Apache USerGrid_ (Mobile Backend as a service)
OpenMeap - not support native
Problem is I could not find out , whether this MDM are provide device identification facility.If any one knows about it please tell me. Device identification may be any unique id or mac address
or If i am going to developed our own middle layer , how we can get mac address from request header. without passing to service method , from the request should take.
is there any way to take sort-out this issue?
Please help me
Here is how OpenMEAP grabs the Mac address and passes it in the header.
package com.openmeap.thinclient;
import java.io.ByteArrayInputStream;
import com.openmeap.http.HttpRequestExecuter;
import com.openmeap.http.HttpResponse;
import com.openmeap.util.Utils;
public class FirstRunCheck implements Runnable {
private SLICConfig config;
private String macAddress;
private HttpRequestExecuter executer;
public FirstRunCheck(SLICConfig config, String macAddress, HttpRequestExecuter executer) {
this.config = config;
this.macAddress = macAddress;
this.executer = executer;
}
public void run() {
if( config.isDevelopmentMode().equals(Boolean.TRUE) ) {
return;
}
if( config.getNotFirstRun()==null ) {
config.setNotFirstRun(Boolean.TRUE);
try {
String macWithSalt = macAddress+".OPENMEAP#$!#3__234";
String hashValue = Utils.hashInputStream("sha1", new ByteArrayInputStream(macWithSalt.getBytes("UTF-8")));
HttpResponse response = executer.get("http://usage.openmeap.com/tracker.gif?hash="+hashValue);
Utils.consumeInputStream(response.getResponseBody());
} catch( Exception ioe ) {
return;
}
}
}
}
I'm trying out a GAE based backend using the sample code on the page below:
https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints
Have been able to deploy the backend and I can view & execute the APIs on API explorer through appspot.com link - project-id.appspot.com
When I execute the client app (Android based) and call
regService.register(regId).execute();
On the server side I get the following log entry on Google Developer Console -
"POST /registration/v1/registerDevice/APA91bHCCvjkMFdvf6YHh_rbdqdKMYoRnwm6iswQtTpztwCfNVWq_7xwSq1y9naiipYmfTrREInybypeLb5mc7LCzYGBSpC9jFM-Co_6xGUBiEjLyo1UT375ak7p0nrOiTdHFNwW7r31WYQJP7ojigRLxBTYvST4XTeNIufD6GHb3SbDFGl1hsc HTTP/1.1" 404 0 - "20773xxxxxxx Google-HTTP-Java-Client/1.17.0-rc (gzip)" "verlllll-auyyyy-zzz.appspot.com" ms=19 cpu_ms=0 app_engine_release=1.9.7 trace_id=ddfbcf4e13e27e2aa2a6c5e77bb8cc6f
where:
registration/v1/registerDevice/ are API/version/Method of the backend service
APA91bHCCvjkMFdvf6YHh_rbdqdKMYoRnwm6iswQtTpztwCfNVWq_7xwSq1y9naiipYmfTrREInybypeLb5mc7LCzYGBSpC9jFM-Co_6xGUBiEjLyo1UT375ak7p0nrOiTdHFNwW7r31WYQJP7ojigRLxBTYvST4XTeNIufD6GHb3SbDFGl1hsc
.. is the device registration id returned by gcm.register(SENDER_ID); gcm is of type GoogleCloudMessaging
20773xxxxxxx or SENDER_ID is the Project number provided on the Google Developer Console.
& verlllll-auyyyy-zzz.appspot.com is the Project Id.
Can you please tell me why am I getting HTTP/1.1 404 in the response?
Thanks in advance..
Sharing the building of regService --
public class GcmRegistrationAsync extends AsyncTask<Context, Void, String> {
private Registration regService; // A Stub API from Server
private GoogleCloudMessaging gcm;
private Context context;
// TODO: change to your own sender ID to Google Developers Console project
// number, as per instructions above
private static final String SENDER_ID = "20773xxxxxxx";
public void GcmRegistrationAsyncTask(int i) {
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
// Need setRootUrl and setGoogleClientRequestInitializer only for local testing,
// otherwise they can be skipped
.setRootUrl("https://verlllll-auyyyy-zzz.appspot.com")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
i = this.test();
builder.setApplicationName(SENDER_ID);
regService = builder.build();
}
public int test () {
return 1;
}
#Override
protected String doInBackground(Context... params) {
int i = params.length;
context = (Context) params[0];
String msg = "test";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
String regId = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regId;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// The request to your server should be authenticated if your app
// is using accounts.
regService.register(regId).execute();
} catch (IOException ex) {
ex.printStackTrace();
msg = "Error: " + ex.getMessage();
}
return msg;
}
}
In MyActivity.java
protected void onCreate(Bundle savedInstanceState) {
:
:
gcmRegistrationAsync = new GcmRegistrationAsync();
gcmRegistrationAsync.GcmRegistrationAsyncTask(1);
gcmRegistrationAsync.execute(this);
}
To connect to an endpoint you have to connect with https, if you connect with http like you did (in your setUrl) then you get the 404 error.
Also your code can probably look more like :
Registration.Builder builder = new Registration.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null);
The rootUrl doesn't need be set, by default it uses your appspot location, when running locally, it's useful to direct the endpoint to your devapp server (app engine local testing server).
The code that disables compression is also just used to be compatible with the devapp server
i would like to use apigee android sdk in android app. By using android sdk, I would like to connect apigee end point proxy but api proxy has got Oauth 2.0 verification. How to access our proxy?
//Create client entity
String ORGNAME = "your-org";
String APPNAME = "your-app";
ApigeeClient apigeeClient = new ApigeeClient(ORGNAME,APPNAME);
DataClient dataClient = apigeeClient.getDataClient();
String type = "item"; //entity type to be retrieved
Map queryString = null; //we don't need any additional query parameters, in this case
//call getCollectionAsync to initiate the asynchronous API call
dataClient.getCollectionAsync(type, queryString, new ApiResponseCallback() {
//If getEntitiesAsync fails, catch the error
#Override
public void onException(Exception e) {
// Error
}
//If getCollectionAsync is successful, handle the response object
#Override
public void onResponse(ApiResponse response) {
try {
if (response != null) {
// Success
}
} catch (Exception e) { //The API request returned an error
// Fail
}
}
});
There is currently no support for OAuth in the Android SDK.
I want to transfer files between 2 devices via Wi-Fi Direct.
I wanted to do the same thing as in WifiDirectDemo, but I can't transfer data from the group owner to the other device, so I tried this: each time when one of the devices clicks connect, the other device is set as the group owner, so on each connection the device who asks for connection is always the client and can send data.
The problem with this is that Android always remembers the first group created and therefore its group owner. In other words, what I did only works the first time unless I go to settings and forget the group created by the first connection.
I know that by using the disconnect button, the Wi-Fi group is removed, but the Android system puts it in remembered groups and uses its setting (group owner negotiation) when a new connection is to be made.
The second thing I tried was to create a ServerSocket on each device (on another port), so this way both the group owner and the other device would be clients and servers at the same time. I don't know if the group owner can be set as a client, but I cant create a ServerSocket on both devices. Here is my code:
<pre>
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
this.info = info;
this.getView().setVisibility(View.VISIBLE);
// The owner IP is now known.
TextView view = (TextView) mContentView.findViewById(R.id.group_owner);
view.setText( getResources().getString(R.string.group_owner_text)
+ ((info.isGroupOwner == true) ? getResources().getString(R.string.yes)
: getResources().getString(R.string.no)));
// InetAddress from WifiP2pInfo struct.
view = (TextView) mContentView.findViewById(R.id.device_info);
view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress());
// After the group negotiation, we assign the group owner as the file
// server. The file server is single threaded, single connection server
// socket.
if (info.groupFormed && info.isGroupOwner) {
new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text),8988)
.execute();
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
Log.d(WiFiDirectActivity.TAG, "serveur8988cree");
} else if (info.groupFormed) {
// The other device acts as the client. In this case, we enable the
// Get file button.
// In this case we create a server socket on another port
new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text),8987)
.execute();
mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE);
Log.d(WiFiDirectActivity.TAG, "serveur8987cree");
((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources()
.getString(R.string.client_text));
}
</pre>
Thanks for help.
You can delete all groups through reflection but, it's bit of a hack and class members might change later
private void deletePersistentInfo() {
try {
Class persistentInterface = null;
//Iterate and get class PersistentGroupInfoListener
for (Class<?> classR : WifiP2pManager.class.getDeclaredClasses()) {
if (classR.getName().contains("PersistentGroupInfoListener")) {
persistentInterface = classR;
break;
}
}
final Method deletePersistentGroupMethod = WifiP2pManager.class.getDeclaredMethod("deletePersistentGroup", new Class[]{Channel.class, int.class, ActionListener.class});
//anonymous class to implement PersistentGroupInfoListener which has a method, onPersistentGroupInfoAvailable
Object persitentInterfaceObject =
java.lang.reflect.Proxy.newProxyInstance(persistentInterface.getClassLoader(),
new java.lang.Class[]{persistentInterface},
new java.lang.reflect.InvocationHandler() {
#Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws java.lang.Throwable {
String method_name = method.getName();
if (method_name.equals("onPersistentGroupInfoAvailable")) {
Class wifiP2pGroupListClass = Class.forName("android.net.wifi.p2p.WifiP2pGroupList");
Object wifiP2pGroupListObject = wifiP2pGroupListClass.cast(args[0]);
Collection<WifiP2pGroup> wifiP2pGroupList = (Collection<WifiP2pGroup>) wifiP2pGroupListClass.getMethod("getGroupList", null).invoke(wifiP2pGroupListObject, null);
for (WifiP2pGroup group : wifiP2pGroupList) {
deletePersistentGroupMethod.invoke(wifiP2pManager, channel, (Integer) WifiP2pGroup.class.getMethod("getNetworkId").invoke(group, null), new ActionListener() {
#Override
public void onSuccess() {
//All groups deleted
}
#Override
public void onFailure(int i) {
}
});
}
}
return null;
}
});
Method requestPersistentGroupMethod =
WifiP2pManager.class.getDeclaredMethod("requestPersistentGroupInfo", new Class[]{Channel.class, persistentInterface});
requestPersistentGroupMethod.invoke(wifiP2pManager, channel, persitentInterfaceObject);
} catch (Exception ex) {
ex.printStackTrace();
}
}
To send data you need to know the IP address (not the device address) of the receiver. For the P2P client, the IP address of group_owner is available in the WifiP2pInfo variable, so it can use this to send data to the group owner. If the group owner knows the IP address of the P2P client to which it wants to send data, then it can also send files. This can be achieved in two ways.
Group owner assigns the IP addresses to the clients and stores the information about it.
Every newly added client sends its IP address to the group owner at the time of joining the group.