Categories
discuss

Android 6.0 requests pairing with remote devices when fetching UUIDS

I’m trying to fetch the UUIDS from a remote bluetooth device like this:

        device.fetchUuidsWithSdp();

This will work silently and without user interaction on all devices except those with Android 6.0 which visibly ask with a pairing dialog to connect with the remote device to fetch the UUID. Is that an expected behaviour? Where is this documented? Is there a way to trigger UUID discovery without explicitly having to allow it from the other end?

Answer

I’ve found a workaround to this by using the hidden sdpSearch method instead of fetchUuidsWithSdp. This requires a bit of reflection. This worked for me on android 6.0 and 5.1.1, without the devices trying to pair. Hope this helps, and feel free to improve the rather poor exception handling.

public class DeviceFinder{

public interface Callback{
    void onDeviceFound(BluetoothDevice bd);
    void onFinishedCallback();
    void onStartCallback();
}

private ArrayList<BluetoothDevice> tempDevices = new ArrayList<>();
private Callback mCallback;
private Context mContext;
private String ACTION_SDP_RECORD;
private String EXTRA_SDP_SEARCH_RESULT;

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)){
            // Aggregating found devices
            BluetoothDevice bd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            tempDevices.add(bd);
        }else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
            // Prepare for new search
            tempDevices = new ArrayList<>();
            mCallback.onStartCallback();
        }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
            // Do a sdpSearch for all found devices
            for (BluetoothDevice bd : tempDevices){
                try {
                    Method m = bd.getClass().getDeclaredMethod("sdpSearch", ParcelUuid.class);
                    m.invoke(bd, new ParcelUuid(/* your uuid here */));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            mCallback.onFinishedCallback();
        }else if( ACTION_SDP_RECORD.equals(action)){
            // check if the device has the specified uuid
            BluetoothDevice bd = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if (intent.getIntExtra(EXTRA_SDP_SEARCH_RESULT, 1) == 0){
                mCallback.onDeviceFound(bd);
            }
        }
    }
};


public DeviceFinder(Context context, Callback mCallback){
    this.mCallback = mCallback;
    this.mContext = context;

    try {
        Field f = BluetoothDevice.class.getDeclaredField("ACTION_SDP_RECORD");
        ACTION_SDP_RECORD = ((String)f.get(null));
        f = BluetoothDevice.class.getDeclaredField("EXTRA_SDP_SEARCH_STATUS");
        EXTRA_SDP_SEARCH_RESULT = ((String)f.get(null));
    } catch (Exception e) {
        e.printStackTrace();
    }


    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    intentFilter.addAction(ACTION_SDP_RECORD);
    context.registerReceiver(mReceiver, intentFilter);
    startScan();
}

public void startScan(){
    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (!bluetoothAdapter.isDiscovering()) {
        bluetoothAdapter.startDiscovery();
    }
}

public void unregisterReciever(){
    mContext.unregisterReceiver(mReceiver);
}
}

edit: sdpSearch was added in android 6.0, so it does not work for earlier versions

Categories
discuss

AngularJS POST Fails: Response for preflight has invalid HTTP status code 404

I know there are a lot of questions like this, but none I’ve seen have fixed my issue. I’ve used at least 3 microframeworks already. All of them fail at doing a simple POST, which should return the data back:

The angularJS client:

var app = angular.module('client', []);

app.config(function ($httpProvider) {
  //uncommenting the following line makes GET requests fail as well
  //$httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = '*';
  delete $httpProvider.defaults.headers.common['X-Requested-With'];
});

app.controller('MainCtrl', function($scope, $http) {
  var baseUrl = 'http://localhost:8080/server.php'

  $scope.response = 'Response goes here';

  $scope.sendRequest = function() {
    $http({
      method: 'GET',
      url: baseUrl + '/get'
    }).then(function successCallback(response) {
      $scope.response = response.data.response;
    }, function errorCallback(response) { });
  };

  $scope.sendPost = function() {
    $http.post(baseUrl + '/post', {post: 'data from client', withCredentials: true })
    .success(function(data, status, headers, config) {
      console.log(status);
    })
    .error(function(data, status, headers, config) {
      console.log('FAILED');
    });
  }
});

The SlimPHP server:

<?php
    require 'vendor/autoload.php';

    $app = new SlimSlim();
    $app->response()->headers->set('Access-Control-Allow-Headers', 'Content-Type');
    $app->response()->headers->set('Content-Type', 'application/json');
    $app->response()->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    $app->response()->headers->set('Access-Control-Allow-Origin', '*');

    $array = ["response" => "Hello World!"];

    $app->get('/get', function() use($array) {
        $app = SlimSlim::getInstance();

        $app->response->setStatus(200);
        echo json_encode($array);
    }); 

    $app->post('/post', function() {
        $app = SlimSlim::getInstance();

        $allPostVars = $app->request->post();
        $dataFromClient = $allPostVars['post'];
        $app->response->setStatus(200);
        echo json_encode($dataFromClient);
    });

    $app->run();

I have enabled CORS, and GET requests work. The html updates with the JSON content sent by the server. However I get a

XMLHttpRequest cannot load http://localhost:8080/server.php/post. Response for preflight has invalid HTTP status code 404

Everytime I try to use POST. Why?

EDIT: The req/res as requested by Pointy req/res headers

Answer

EDIT:

It’s been years, but I feel obliged to comment on this further. Now I actually am a developer. Requests to your back-end are usually authenticated with a token which your frameworks will pick up and handle; and this is what was missing. I’m actually not sure how this solution worked at all.

ORIGINAL:

Ok so here’s how I figured this out. It all has to do with CORS policy. Before the POST request, Chrome was doing a preflight OPTIONS request, which should be handled and acknowledged by the server prior to the actual request. Now this is really not what I wanted for such a simple server. Hence, resetting the headers client side prevents the preflight:

app.config(function ($httpProvider) {
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
});

The browser will now send a POST directly. Hope this helps a lot of folks out there… My real problem was not understanding CORS enough.

Link to a great explanation: http://www.html5rocks.com/en/tutorials/cors/

Kudos to this answer for showing me the way.

Categories
discuss

Putting content below AppBarLayout in a CoordinatorLayout

I’m very new to Android and I intended to post this to the Android Developers – Google Groups but they seem to say that newbies need to post to Stack Overflow. So I’m here.

I downloaded the most recent version of Android Studio 1.4.1 yesterday, and I followed the instructions on Building Your First App. I did everything up to Starting Another Activity. It seems these instructions are a bit old i.e. for a previous version of the SDK, because they do not reference CoordinatorLayout and AppBarLayout though they appear in the code if you follow the steps. Obviously, I did make minor changes in the code to get this app to work, but not completely.

My Problem: If you look at the images at the bottom of Starting Another Activity you will see that both of them have the title My First App. In my modifications of the code, I could not get this title on both the images/screens. (I should mention that I want to use the more recent version of AppBarLayout and CoordinatorLayout)

Let’s focus on the first screen, the activity_my.xml is

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent"
android:layout_height="match_parent" 
android:fitsSystemWindows="true"
tools:context=".MyActivity">

<include layout="@layout/content_my" />

<android.support.design.widget.FloatingActionButton 
    android:id="@+id/fab"
    android:layout_width="wrap_content"     
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end" 
    android:layout_margin="@dimen/fab_margin"
    android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

As mentioned at the bottom of Building a Simple User Interface the content_my.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="horizontal" >

<EditText android:id="@+id/edit_message"
    android:layout_weight="1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:hint="@string/edit_message" />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button_send"
    android:onClick="sendMessage"/>
</LinearLayout>

Is there anyway, I can add the AppBarLayout to the activity_my.xml. I have tried something like:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent"
android:layout_height="match_parent" 
android:fitsSystemWindows="true"
tools:context=".MyActivity">

<android.support.design.widget.AppBarLayout 
    android:layout_height="wrap_content"
    android:layout_width="match_parent" 
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar 
        android:id="@+id/toolbar"
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary" 
        app:popupTheme="@style/AppTheme.PopupOverlay" />

</android.support.design.widget.AppBarLayout>


<include layout="@layout/content_my" />

<android.support.design.widget.FloatingActionButton 
    android:id="@+id/fab"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end" 
    android:layout_margin="@dimen/fab_margin"
    android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

The problem with this is that the content in content_my.xml goes behind the Toolbar of AppBarLayout rather than below it. Any ideas how to fix this issue?

Answer

Layouts in a CoordinatorLayout need to define a layout_behavior. Change your content to this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" 
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>

<EditText android:id="@+id/edit_message"
    android:layout_weight="1"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:hint="@string/edit_message" />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/button_send"
    android:onClick="sendMessage"/>
</LinearLayout>
Categories
discuss

How can I change a View’s visibility based on whether a list is empty or not

I have a List in a model that bounds to a layout using the data-binding lib. How can I change a View‘s visibility based on the list’s isEmpty() condition? Something like android:visibility="@{model.list.isEmpty() ? View.INVISIBLE : View.VISIBLE}"

Answer

Ok, although it wasn’t clear from the docs, it can be done using custom setters as follows:


In my model, I had to declare

@BindingAdapter("android:visibility")
public static void setVisibility(View view, Model model) {

        view.setVisibility(model.getList().isEmpty() ? View.INVISIBLE: View.VISIBLE);
}

Then, in the layout

 android:visibility="@{model}"

where model is the name of the model’s variable in <variable name=""/>

Categories
discuss

Mass vector assets import Android Studio

Is there a way to import many vector svg images into project? Importing 30+ icons is kind of boring and rather stupid. Which script is Android Studio using to convert svgs?

Answer

Well in case anyone will be interested there are online tool and offline .NET/Mono tool for mass import. The last one is more reliable and flexible. Or we can use shell script.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..