Categories
discuss

How can I use Retrofit to POST a complex JSON parameter

The Post request i need to do should look like this

{
"project": {
    "name": "newname123",
    "identifier": "id55"},
"key":"8f583ad25100575b974062e0cee43e47aa158e4e"}

I was able to send it to server using raw implementation in Postman, but have no idea how to send the same using form-data

Here is my interface

@FormUrlEncoded
@POST("projects.json")
Call<Project> CreateProject(@Field(value = "project") ProjectToSend project,
                            @Field("key") String key);

No matter if I tried to do it with @body or @field, it does not work.

ProjectToSend class

public class ProjectToSend {
private String name;
private String identifier;

public ProjectToSend(String name, String identifier) {
    this.name = name;
    this.identifier = identifier;

Answer

create two classes, to represent the complete JSON, something like this:

public class Project {
    private ProjectToSend project;
    private String key;

    // getters and setters
}

public class ProjectToSend {
    private String name;
    private String identifier;

    // getters and setters
}

and then call:

@POST("put/the/url/here")
Call<Project> createUser(@Body Project project);
Categories
discuss

How to keep 1:1 aspect ratio video all the time in WebRTC

When I use this setting, the video aspect ratio is 1:1.

constraints = {
  audio: false,
  video: { width: 240, height: 240 }
};

However, I want WebRTC choose better resolution if exists. When I changed it to this

constraints = {
  audio: false,
  video: {
    width: { min: 240, ideal: 720, max: 1080 },
    height: { min: 240, ideal: 720, max: 1080 }
  }
};

The demo jsfiddle

In my case, sometimes, it becomes 4:3, which is 640*480. I think it is because both the number 640 and 480 are between 240 and 1080.

How can I keep it 1:1 all the time? Thanks

Answer

You’re asking about getUserMedia here, which is effectively about camera resolutions.

What resolutions your camera captures video in depends on your hardware. My camera for instance, can capture in a number of modes, they’re all 4:3 modes up to about 640×480, and above that they’re all 16:9. It has no 1:1 modes (I checked).

You haven’t said why you care about aspect, but I suspect you still want a ball to appear round and not oval, so if your site wants video from my camera to fit in a square, it’ll need to either crop out the left and right parts of my video or suffer black bars above and below. No way around it.

If it’s just about layout, leave the capture aspect alone and crop the playback element (which scales the video for you btw) with CSS instead. This gives you full control (use https fiddle in Chrome):

navigator.mediaDevices.getUserMedia({ video: true })
  .then(stream => video.srcObject = stream)
  .then(() => new Promise(resolve => video.onloadedmetadata = resolve))
  .then(() => log(video.srcObject.getVideoTracks()[0].label +
                  " ("+ video.videoWidth +"x"+ video.videoHeight +")"))
  .catch(log);

var log = msg => div.innerHTML += msg + "<br>";
.crop{
  overflow:hidden;
  display:block;
  width: 120px;
}
#video{
  margin-left: -15px;
}
<div class="crop"><video id="video" width="160" height="120" autoplay></video></div>
<br><div id="div"></div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

Also, please be aware of browser differences here, some browsers (Chrome) will mutate (crop) the camera capture for you, while others (Firefox) will always give you native modes.

Categories
discuss

Android how to open fragment from listview element

In MainActivity is FrameLayout MainContainer. I load there a fragment TrainerMyGroups, there is a Listview where I add a few elements (each element has some strings) by use TrainerGroupsAdapter. Actually I want to replace fragment TrainerMyGroups by another (for example TrainersInfo) by click on list’s element.

My TrainerGroupsAdapter is:

public class TrainerGroupsAdapter extends ArrayAdapter {

    List list = new ArrayList();

    public TrainerGroupsAdapter(Context context, int resource) {
        super(context, resource);
    }

    static class Datahandler{
        TextView name;
        TextView when;
        TextView where;
        LinearLayout ll;
    }

    @Override
    public void add(Object object) {
        super.add(object);
        list.add(object);
    }

    @Override
    public int getCount() {
        return this.list.size();
    }

    @Override
    public Object getItem(int position) {
        return this.list.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row;
        row=convertView;
        Datahandler handler;
        SharedPreferences pref = getContext().getSharedPreferences("pref", Context.MODE_PRIVATE);

        if(convertView==null){
            LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            row= inflater.inflate(R.layout.list_mygroups,parent,false);
            handler = new Datahandler();
            handler.name =  (TextView) row.findViewById(R.id.trainermygroupslistname);
            handler.where =  (TextView) row.findViewById(R.id.trainermygroupslistwhere);
            handler.when =  (TextView) row.findViewById(R.id.trainermygroupslistwhen);
            handler.ll=(LinearLayout) row.findViewById(R.id.trainermygroupslistlayout);
            row.setTag(handler);
        }
        else {
            handler = (Datahandler)row.getTag();
        }

        TrainerGroupsDataProvider dataProvider;
        dataProvider = (TrainerGroupsDataProvider)this.getItem(position);
        handler.name.setText(dataProvider.getName());
        handler.when.setText(dataProvider.getWhen());
        handler.where.setText(dataProvider.getWhere());
        handler.ll.setBackgroundColor(Color.parseColor(pref.getString(TRANSP_KEY, "#CC") + dataProvider.getColor()));


        handler.ll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
            }
        });


        return row;
    }

}

It doesn’t work but method OnClick is probably good because if I replace getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit(); to code for change some strings (name, when tc) it works. Problem is in getFragmentMenager(), Android Studio shows message that I have to create Getter and AS’s suggestion is to generate in OnClick method:

private FragmentManager fragmentManager;
public FragmentManager getFragmentManager() {
      return fragmentManager;
}

then problem is in the second argument in replace, I have error that it has to be Fragment (Im sure that TrainersInfo() is fragment because I use it in other place and it works). How can I solve this problem or what is the best way to open fragment by click on lise’s element in another fragment?

Answer

#UPDATE2

Better then replacing the Fragment inside the adapter is to say your activity that it should replace the fragment. This can be done with an interface which you implement inside your Activity:

public class TrainerGroupsAdapter extends ArrayAdapter {
    // The interface which you have to implement in your activity
    public interface OnChangeFragmentListener {
        void changeFragment();
    }

    List list = new ArrayList();
    private OnChangeFragmentListener m_onChangeFragmentListener;

    public TrainerGroupsAdapter(Context context, int resource) {
        super(context, resource);
        m_onChangeFragmentListener = (OnChangeFragmentListener) context;
    }

    // Your other code

}

The OnClickListener in your getView Method:

handler.ll.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Call the method which change the fragment
        m_onChangeFragmentListener.changeFragment();
    }
});

The Activity:

public class MainActivity extends AppCompatActivity implements TrainerGroupsAdapter.OnChangeFragmentListener {

    //...
    //  Your Other code

    // Implement the method which is called in the adapter and replace the fragment here
    @Override
    public void changeFragment() {
        getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
    }
}

#UPDATE1

You need an activity context for getSupportFragmentManager() and getFragmentManager(). You can change the Context parameter of the constructor to Activity and create a member variable in your class for the activity so you can use it later:

public class TrainerGroupsAdapter extends ArrayAdapter {

    List list = new ArrayList();
    private Activity m_activity;

    public TrainerGroupsAdapter(Activity context, int resource) {
        super(context, resource);
        m_activity = context;
    }
    // Your other code
}

The OnClickListener in your getView method:

   handler.ll.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            m_activity.getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
        }
   });
Categories
discuss

Android Retrofit Design Patterns

I am using Retrofit to interact with my REST API, and was wondering whether anybody has any design suggestions.

My app has the following packages:

  1. models
  2. services
  3. activities
  4. fragments

The services package contains the interfaces for Retrofit. For example:

public interface FooService {
    @FormUrlEncoded
    @POST("foo/do")
    @Headers("Content-Type: application/x-www-form-urlencoded; charset=UTF-8")
    Call<FooBar> do();
}

Models contains…well, the different models. For example, FooBar. So far so good – just as per Retrofit documentation.

I have created an API class, that handles the Retrofit build logic (Retrofit retrofit = new Retrofit.Builder() etc), and exposes a static field: retrofit.In my activities I then can perform my requests as follows:

FooService service = API.retrofit.create(FooService.class);
Call<FooBar> call = service.do();

try {
    retrofit2.Response response = call.execute();
    // ...do stuff...
} catch(IOException) {}

And herewith comes my question: Would it be better to abstract the above further? So that I would not need to repeat the above everywhere? For example, something like:

MyOtherFooService service = new  MyOtherFooService();
FooBar fooBar = service.do();

Any thoughts? Recommendations?

Answer

I usually use singleton pattern with following structure :

first define ServiceHelper like following :

public class ServiceHelper {

private static final String ENDPOINT = "http://test.com";

private static OkHttpClient httpClient = new OkHttpClient();
private static ServiceHelper instance = new ServiceHelper();
private IPlusService service;


private ServiceHelper() {

    Retrofit retrofit = createAdapter().build();
    service = retrofit.create(IPlusService.class);
}

public static ServiceHelper getInstance() {
    return instance;
}

private Retrofit.Builder createAdapter() {

    httpClient.setReadTimeout(60, TimeUnit.SECONDS);
    httpClient.setConnectTimeout(60, TimeUnit.SECONDS);
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    httpClient.interceptors().add(interceptor);

    return new Retrofit.Builder()
            .baseUrl(ENDPOINT)
            .client(httpClient)
            .addConverterFactory(GsonConverterFactory.create());
}

public Call<List<CategoryModel>> getAllCategory() {
    return service.getAllCategory();
}

Then put all of your services in IService (in my case it’s IPlusService)

    public interface IPlusService {
    //@Headers( "Content-Type: application/json" ) in Post method may use this
    @GET("/api/category")
    Call<List<CategoryModel>> getAllCategory();
}

Then call your singleton like below in your activity/fragment :

ServiceHelper.getInstance().getAllCategory().enqueue(new Callback<List<CategoryModel>>() {
        @Override
        public void onResponse(Response<List<CategoryModel>> response, Retrofit retrofit) {
            processResponse(response);
        }

        @Override
        public void onFailure(Throwable t) {
            processResponse(null);
        }
    });
Categories
discuss

Java stream – groupingBy by a nested list (list in a second order)

I have the following data structure –

List of Students that each holds a lists of States that each holds a list of cities.

public class Student {
    private int id;
    private String name;
    private List<State> states = new ArrayList<>();
}

public class State {
    private int id;
    private String name;
    private List<City> Cities = new ArrayList<>();
}

public class City {
    private int id;
    private String name;
}

I want to get the following.

Map<String, Students> citiesIdsToStudensList;

I write the following

Map<Integer, List<Integer>> statesToStudentsMap = students.stream()
            .flatMap(student -> student.getStates().stream())
            .flatMap(state -> state.getCities().stream())
            .collect(Collectors.groupingBy(City::getId, Collectors.mapping(x -> x.getId(), Collectors.toList())));

But it doesn’t get me the result I want.

Answer

Using the Stream API, you’ll need to flat map twice and map each intermediate student and city into a tuple that is capable of holding the student.

Map<Integer, List<Student>> citiesIdsToStudentsList =
    students.stream()
            .flatMap(student -> student.getStates().stream().map(state -> new AbstractMap.SimpleEntry<>(student, state)))
            .flatMap(entry -> entry.getValue().getCities().stream().map(city -> new AbstractMap.SimpleEntry<>(entry.getKey(), city)))
            .collect(Collectors.groupingBy(
                entry -> entry.getValue().getId(),
                Collectors.mapping(Map.Entry::getKey, Collectors.toList())
            ));

However, it would maybe be cleaner to use nested for loops here:

Map<Integer, List<Student>> citiesIdsToStudentsList = new HashMap<>();
for (Student student : students) {
    for (State state : student.getStates()) {
        for (City city : state.getCities()) {
            citiesIdsToStudentsList.computeIfAbsent(city.getId(), k -> new ArrayList<>()).add(student);
        }
    }
}

This leverages computeIfAbsent to populate the map and creates a list of each student with the same city id.

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..