Categories
discuss

How to bind Service if it’s in another process?

Manifest:

 <service android:name="com.example.MainService" android:process=":main_service"/>

Trying to bind service in Activity:

public class MainActivity extends Activity {
    MainService mMainService;

    private boolean mBound;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        bindService(intentForMainService, mConnection, Context.BIND_AUTO_CREATE)
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            MainService.MainServiceBinder binder = (MainService.MainServiceBinder) service;//HERE IS EXCEPTION
            mMainService = (MainService) binder.getService();
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            mMainService = null;
            mBound = false;
        }
    };

    @Override
    protected void onStop() {
        doUnbindService();
        super.onStop();
    }

    void doUnbindService() {
        if (mBound) {
          unbindService(mConnection);
        }
    }
}

Error:

    FATAL EXCEPTION: main
 Process: com.hos.android, PID: 9001
   java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.example.service.main.MainService$MainServiceBinder
   at com.example.ui.base.BaseServiceActivity$1.onServiceConnected(MainActivity.java:34)
   at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1335)
   at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1352)
   at android.os.Handler.handleCallback(Handler.java:739)
   at android.os.Handler.dispatchMessage(Handler.java:95)
   at android.os.Looper.loop(Looper.java:158)
   at android.app.ActivityThread.main(ActivityThread.java:7224)

But when I delete this android:process=”:main_service” all works properly

Answer

Step #1: Write an AIDL file that describes the interface to be exported by the service that clients can bind to. For the purposes of this answer, I will call this interface Foo, and so the AIDL file would be Foo.aidl. Note that if the client and service are in separate Android Studio modules that both need the same Foo.aidl content.

Step #2: Have your service’s binder extend Foo.Stub and override the methods on Foo.Stub, instead of extending IBinder.

Step #3: In your client, in onServiceConnected(), convert the raw binder to a Foo instance via Foo.Stub.asInterface(service), and Foo has the client side of the AIDL-defined API.

This pair of sample projects illustrates this, where in my case the client and the service are in separate apps.

Categories
discuss

How to retain RecyclerView’s position after Orientation change, while using Firebase & ChildEventListener?

I’m working on a simple APOD app that implements:

  • RecyclerView
  • CardView
  • Firebase
  • Picasso

The app grabs images and text from Firebase and Firebase Storage, displays them in a CardView, and sets an OnClickListener to each View. When the user clicks on an image, I open a new Activity through an Intent. The second Activity displays the original clicked image, and more info about it.

I’ve implemented this all using a GridLayoutManager, 1 column if the user’s phone is VERTICAL, 3 columns if the user’s phone is HORIZONTAL.

The problem I’m having is I can’t seem to save the RecyclerView‘s position on orientation change. I’ve tried every single option that I could find, but none seem to work. The only conclusion I could come up with, is that on rotation, I’m destroying Firebase‘s ChildEventListener to avoid a memory leak, and once orientation is complete, Firebase re-queries the database because of the new instance of ChildEventListener.

Is there any way I can save my RecyclerView‘s position on orientation change? I do not want android:configChanges as it won’t let me change my layout, and I’ve already tried saving as a Parcelable, which was unsuccessful. I’m sure it’s something easy I’m missing, but hey, I’m new to developing. Any help or suggestions on my code is greatly appreciated. Thanks!

Below are my classes, which I have shortened only to the necessary code.

MainActivity

public class MainActivity extends AppCompatActivity {

private RecyclerAdapter mRecyclerAdapter;
private DatabaseReference mDatabaseReference;
private RecyclerView mRecyclerView;
private Query query;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    setContentView(R.layout.activity_main);
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    final int columns = getResources().getInteger(R.integer.gallery_columns);

    mDatabaseReference = FirebaseDatabase.getInstance().getReference();
    query = mDatabaseReference.child("apod").orderByChild("date");

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, columns));


    mRecyclerAdapter = new RecyclerAdapter(this, query);
    mRecyclerView.setAdapter(mRecyclerAdapter);


  }

@Override
public void onDestroy() {
    mRecyclerAdapter.cleanupListener();
  }

}

RecyclerAdapter

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ApodViewHolder> {

private final Context mContext;
private final ChildEventListener mChildEventListener;
private final Query mDatabaseReference;
private final List<String> apodListIds = new ArrayList<>();
private final List<Apod> apodList = new ArrayList<>();

public RecyclerAdapter(final Context context, Query ref) {
    mContext = context;
    mDatabaseReference = ref;


    ChildEventListener childEventListener = new ChildEventListener() {


        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            int oldListSize = getItemCount();
            Apod apod = dataSnapshot.getValue(Apod.class);

            //Add data and IDs to the list
            apodListIds.add(dataSnapshot.getKey());
            apodList.add(apod);

            //Update the RecyclerView
            notifyItemInserted(oldListSize - getItemCount() - 1);
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {
            String apodKey = dataSnapshot.getKey();
            int apodIndex = apodListIds.indexOf(apodKey);

            if (apodIndex > -1) {

                // Remove data and IDs from the list
                apodListIds.remove(apodIndex);
                apodList.remove(apodIndex);

                // Update the RecyclerView
                notifyDataSetChanged();
            }
        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }

    };
    ref.addChildEventListener(childEventListener);
    mChildEventListener = childEventListener;

 }

   @Override
    public int getItemCount() {
    return apodList.size();
}
    public void cleanupListener() {
    if (mChildEventListener != null) {
        mDatabaseReference.removeEventListener(mChildEventListener);
    }
  }

}

Answer

EDIT:

I was finally able to make this work using multiple factors. If any one of these were left out, it simply would not work.

Create new GridLayoutManager in my onCreate

gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns);

Save the RecyclerView state in onPause

private final String KEY_RECYCLER_STATE = "recycler_state"; 
private static Bundle mBundleRecyclerViewState;
private Parcelable mListState = null;

@Override
protected void onPause() {
    super.onPause();

    mBundleRecyclerViewState = new Bundle();
    mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
    mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
}

Include configChanges in the AndroidManifest.xml

Saving and restoring the RecyclerView state was not enough. I also had to go against the grain and change my AndroidManifest.xml to ‘android:configChanges=”orientation|screenSize”‘

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize">

Restore the RecyclerView state in onConfigurationChanged using a Handler

The handler was one of the most important parts, if it’s not included, it simply will not work and the position of the RecyclerView will reset to 0. I guess this has been a flaw going back a few years, and was never fixed. I then changed the GridLayoutManager‘s setSpanCount depending on the orientation.

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    columns = getResources().getInteger(R.integer.gallery_columns);

    if (mBundleRecyclerViewState != null) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
                mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);
            }
        }, 50);
    }

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        gridLayoutManager.setSpanCount(columns);
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        gridLayoutManager.setSpanCount(columns);
    }
    mRecyclerView.setLayoutManager(gridLayoutManager);
}

Like I said, all of these changes needed to be included, at least for me. I don’t know if this is the most efficient way, but after spending many hours, it works for me.

Categories
discuss

Swagger UI causes failure in Multipart-FormData request from spring-resources with file and json-object

I’m using spring-boot in the version 1.4.3 in my project. The Project provides some REST-endpoints and uses springfox-swagger 2.6.1 to generate the api-documentation. This works just fine for almost all of my requests and I can successfully try the endpoints.

However, this stops working on a multipart-formdata request, containing a MultipartFile and an json-object. The problem is similar to the following issue:

JHipster, spring rest service with MultiPart and JSON, swagger/curl error

I got it working in this manner, but for me, it’s not a valid solution to change the json-object to a request parameter due to it’s possible size (during a try with a bigger json-object I got an error telling, that the header is too big).

Here are the sample code, and error message:

Endpoint:

@RequestMapping(path = "/", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE },
  consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, method = RequestMethod.PUT)
@ResponseStatus(code = HttpStatus.CREATED)
public void createApproval(@RequestPart(required = true) @Valid @NotNull @NotBlank MultipartFile file,
  @RequestPart(required = true) MyClass json, HttpServletRequest request, HttpServletResponse response)

This generates a swagger API as follows:

parameter - value              - description - parameter type - data type
file      - (upload file)      - file        - formData       - file
json      - string-value-filed - json        - formData       - undefined

Executing this with valid data generates the following error:

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:237)
at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:127)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:160)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:651)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

I’ve also written a test for it, this works just nice:

final MockHttpServletRequestBuilder saveRequestBuilder = MockMvcRequestBuilders.fileUpload(baseUrl).file(mockFile)
    .file(jsonFile).contentType(contentType);
saveRequestBuilder.with(new RequestPostProcessor() {
  @Override
  public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
    request.setMethod("PUT");
    return request;
  }
});
mockMvc.perform(saveRequestBuilder).andExpect(status().isCreated()).andDo( and so on...

I Also tried some changes with @ApiImplicitParam. Most of them didn’t change anything. using the following change, I got a different API, but an error then tells me that’s the json-object is not present.

@ApiImplicitParam(name = "json", value = "the json object", required = true,
  dataType = "MyClass", paramType = "body")

So, does anyone know how to get this to work during the swagger-ui-doc?

Thanks for your help beforehand!

Answer

Never mind, I just found a solution. As it seems, I already linked it in my question.

I switched the json-parameter to a simple string and converted it manually to my class works just fine. So the solution in the linked Question works fine here, too.

So a short Lessons learned:

Spring-Swagger-Combination has some problems in multipart request to convert json-input to an object. A workaround is using a simple string and convert it by yourself. I hope they will make it work in the future.

Additionally to the linked solution: It doesn’t has to be a RequestParam, RequestPart works fine, too.

Categories
discuss

How to include jQuery properly in angular cli 1.0.0-rc.0?

I use jQuery-based select2 component in my AngularJS project. I had similar issue as guys here: https://github.com/fronteed/icheck/issues/322, and solved it using advice from there. To be accurate, I received error TypeError: $(...).select2 is not a function when not using that advice.

i.e. I added next lines to configuration of Webpack in @angular/cli/models/webpack-configs/common.js.

plugins: [
  new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })
]

Is it the best possible way to enable jQuery in angular/cli?

I don’t think that doing same as in https://github.com/angular/angular-cli/wiki/stories-global-lib is correct way, because

a) webpack bundles jQuery without need to specify it in scripts

b) it still throws TypeError: $(...).select2 is not a function error when you include it as described in story.

Answer

I was able to achieve what I needed by exposing Webpack. Use ng eject command.

First, install jQuery using npm install -S jquery (and I also installed npm install -S select2, because I needed that too).

Then, make sure that you made backup of your package.json file, since during ng eject Angular CLI will try to add its Webpack dependencies inside your package.json.

After ng eject finished working, inside webpack.config.js I added

// ... Inside require/imports part
const ProvidePlugin = require('webpack/lib/ProvidePlugin');

// ... Inside `plugins` section of Webpack configuration
new ProvidePlugin({
      $: "jquery",
      jQuery: "jquery"
    })

And now select2 adds $(...).select2 function to global jQuery object properly!

To use jQuery with select2 inside your .ts file, you can add next lines at the beginning of that file:

import "select2";
import "jquery";
declare var $: any;

// Somewhere deep within component
// ...

$(this.input.nativeElement)
    .select2(config)
    .on("change select2:select select2:unselect", (e: any) => this.onSelect2ElementChange(e));

// ...
Categories
discuss

Listener on a Spinner

This code adds strings to a Spinner from a list.
How do I implement a listener so that I can get the string that is clicked or tapped from the Spinner?

spinner = (Spinner) findViewById(R.id.spinner);
List<String> list = new ArrayList<String>();

ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_spinner_item, list);
    dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

    spinner.setAdapter(dataAdapter);

Answer

You can use this code

 spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) 
    {
        String selectedItem = parent.getItemAtPosition(position).toString(); //this is your selected item
    }
    public void onNothingSelected(AdapterView<?> parent) 
    {

    }           
});
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..