Im trying to test the Performance tip of : https://groups.google.com/forum/#!msg/google-web-toolkit/f9FLCEloW-c/3ZelaqGUGTcJ
I have more than 5 different Callbacks with different result object .
how can i creat a dynamic static class in only one static Class :
public class AsyncCallbacks
{
private static AsyncCallback<?> callback = null;
private AsyncCallbacks(){
}
private AsyncCallback<?> createCallback() {
if(callback == null) {
callback = new AsyncCallback(){
#Override
public void onFailure(Throwable caught)
{
// TODO Auto-generated method stub
}
#Override
public void onSuccess(Object result)
{
// TODO Auto-generated method stub
}
};
}
return callback;
}
}
public class Asyncs implements AsyncCallback<Object> {
private Dto1 dto1 = null;
private Dto2 dto2 = null;
private Object result = null;
#Override
public void onFailure(Throwable caught) {
// failed message
}
#Override
public void onSuccess(Object result) {
this.result = result;
}
public Object getSuccessObject() {
if (result instanceof Dto1) {
dto1 = (Dto1) result;
} else if (result instanceof Dto2) {
dto2 = (Dto2) result;
}
return result;
}
}
Related
We are trying to implement SMART On FHIR healthcare authorization protocol specification. This spec is an extension to OIDC (open id connect protocol). In SMART on FHIR, we need to add extra claims called 'patient' with value say '123' in AccessTokenResponse object during the OAUTH dance.
In order to accomplish this, I tried to extended the OIDCLoginProtocol and OIDCLoginProtocolFactory classes and given a new name to this protocol called 'smart-openid-connect'. I created this as a SPI (service provider interface) JAR and copied it to /standalone/deployments folder. Now, I can see the new protocol called 'smart-openid-connect' in the UI, but it does not show Access Type options in the client creation screen to select as a confidential client. Hence, I am not able to create client secrets as the Credentials menu is not appearing for this new protocol.
I have the following questions:
How to enable the Credentials tab in the client creation screen using SPI for the new protocol that I created.?
Which class I need to override to add extra claims in AccessTokenResponse ?
Kindly help me in this regard.
Thanks for your help in advance.
I have followed your steps for developing our custom protocol. When we migrate our company existed authentication protocol, I have used org.keycloak.adapters.authentication.ClientCredentialsProvider, org.keycloak.authentication.ClientAuthenticatorFactory, org.keycloak.authentication.ClientAuthenticator classes for defining our custom protocol. Credentials tab is only visible if oidc and confidential choices are selected. It is defined on UI javascript codes. So we choose oidc option for setting custom protocol. Afterwards, we return back to our custom protocol.
XyzClientAuthenticatorFactory
public class XyzClientAuthenticatorFactory implements ClientAuthenticatorFactory, ClientAuthenticator {
public static final String PROVIDER_ID = "xyz-client-authenticator";
public static final String DISPLAY_TEXT = "Xyz Client Authenticator";
public static final String REFERENCE_CATEGORY = null;
public static final String HELP_TEXT = null;
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED,
AuthenticationExecutionModel.Requirement.ALTERNATIVE,
AuthenticationExecutionModel.Requirement.DISABLED};
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(Constants.CLIENT_SETTINGS_APP_ID);
property.setLabel("Xyz App Id");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(Constants.CLIENT_SETTINGS_APP_KEY);
property.setLabel("Xyz App Key");
property.setType(ProviderConfigProperty.STRING_TYPE);
configProperties.add(property);
}
#Override
public void authenticateClient(ClientAuthenticationFlowContext context) {
}
#Override
public String getDisplayType() {
return DISPLAY_TEXT;
}
#Override
public String getReferenceCategory() {
return REFERENCE_CATEGORY;
}
#Override
public ClientAuthenticator create() {
return this;
}
#Override
public boolean isConfigurable() {
return false;
}
#Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return REQUIREMENT_CHOICES;
}
#Override
public boolean isUserSetupAllowed() {
return false;
}
#Override
public List<ProviderConfigProperty> getConfigPropertiesPerClient() {
return configProperties;
}
#Override
public Map<String, Object> getAdapterConfiguration(ClientModel client) {
Map<String, Object> result = new HashMap<>();
result.put(Constants.CLIENT_SETTINGS_APP_ID, client.getAttribute(Constants.CLIENT_SETTINGS_APP_ID));
result.put(Constants.CLIENT_SETTINGS_APP_KEY, client.getAttribute(Constants.CLIENT_SETTINGS_APP_KEY));
return result;
}
#Override
public Set<String> getProtocolAuthenticatorMethods(String loginProtocol) {
if (loginProtocol.equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
Set<String> results = new LinkedHashSet<>();
results.add(Constants.CLIENT_SETTINGS_APP_ID);
results.add(Constants.CLIENT_SETTINGS_APP_KEY);
return results;
} else {
return Collections.emptySet();
}
}
#Override
public String getHelpText() {
return HELP_TEXT;
}
#Override
public List<ProviderConfigProperty> getConfigProperties() {
return new LinkedList<>();
}
#Override
public ClientAuthenticator create(KeycloakSession session) {
return this;
}
#Override
public void init(Config.Scope config) {
}
#Override
public void postInit(KeycloakSessionFactory factory) {
}
#Override
public void close() {
}
#Override
public String getId() {
return PROVIDER_ID;
}
}
XyzClientCredential
public class XyzClientCredential implements ClientCredentialsProvider {
public static final String PROVIDER_ID = "xyz-client-credential";
#Override
public String getId() {
return PROVIDER_ID;
}
#Override
public void init(KeycloakDeployment deployment, Object config) {
}
#Override
public void setClientCredentials(KeycloakDeployment deployment, Map<String, String> requestHeaders, Map<String, String> formParams) {
}
}
XyzLoginProtocolFactory
public class XyzLoginProtocolFactory implements LoginProtocolFactory {
static {
}
#Override
public Map<String, ProtocolMapperModel> getBuiltinMappers() {
return new HashMap<>();
}
#Override
public Object createProtocolEndpoint(RealmModel realm, EventBuilder event) {
return new XyzLoginProtocolService(realm, event);
}
protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) {
addDefaultClientScopes(realm, Arrays.asList(newClient));
}
protected void addDefaultClientScopes(RealmModel realm, List<ClientModel> newClients) {
Set<ClientScopeModel> defaultClientScopes = realm.getDefaultClientScopes(true).stream()
.filter(clientScope -> getId().equals(clientScope.getProtocol()))
.collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
for (ClientScopeModel defaultClientScopeModel : defaultClientScopes) {
newClient.addClientScope(defaultClientScopeModel, true);
}
}
Set<ClientScopeModel> nonDefaultClientScopes = realm.getDefaultClientScopes(false).stream()
.filter(clientScope -> getId().equals(clientScope.getProtocol()))
.collect(Collectors.toSet());
for (ClientModel newClient : newClients) {
for (ClientScopeModel nonDefaultClientScope : nonDefaultClientScopes) {
newClient.addClientScope(nonDefaultClientScope, true);
}
}
}
#Override
public void createDefaultClientScopes(RealmModel newRealm, boolean addScopesToExistingClients) {
// Create default client scopes for realm built-in clients too
if (addScopesToExistingClients) {
addDefaultClientScopes(newRealm, newRealm.getClients());
}
}
#Override
public void setupClientDefaults(ClientRepresentation rep, ClientModel newClient) {
}
#Override
public LoginProtocol create(KeycloakSession session) {
return new XyzLoginProtocol().setSession(session);
}
#Override
public void init(Config.Scope config) {
log.infof("XyzLoginProtocolFactory init");
}
#Override
public void postInit(KeycloakSessionFactory factory) {
factory.register(event -> {
if (event instanceof RealmModel.ClientCreationEvent) {
ClientModel client = ((RealmModel.ClientCreationEvent)event).getCreatedClient();
addDefaultClientScopes(client.getRealm(), client);
addDefaults(client);
}
});
}
protected void addDefaults(ClientModel client) {
}
#Override
public void close() {
}
#Override
public String getId() {
return XyzLoginProtocol.LOGIN_PROTOCOL;
}
}
XyzLoginProtocol
public class XyzLoginProtocol implements LoginProtocol {
public static final String LOGIN_PROTOCOL = "xyz";
protected KeycloakSession session;
protected RealmModel realm;
protected UriInfo uriInfo;
protected HttpHeaders headers;
protected EventBuilder event;
public XyzLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, EventBuilder event) {
this.session = session;
this.realm = realm;
this.uriInfo = uriInfo;
this.headers = headers;
this.event = event;
}
public XyzLoginProtocol() {
}
#Override
public XyzLoginProtocol setSession(KeycloakSession session) {
this.session = session;
return this;
}
#Override
public XyzLoginProtocol setRealm(RealmModel realm) {
this.realm = realm;
return this;
}
#Override
public XyzLoginProtocol setUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
return this;
}
#Override
public XyzLoginProtocol setHttpHeaders(HttpHeaders headers) {
this.headers = headers;
return this;
}
#Override
public XyzLoginProtocol setEventBuilder(EventBuilder event) {
this.event = event;
return this;
}
#Override
public Response authenticated(AuthenticationSessionModel authSession, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
log.debugf("Authenticated.. User: %s, Session Id: %s", userSession.getUser().getUsername(), userSession.getId());
try {
....
} catch (Exception ex) {
// TODO handle TokenNotFoundException exception
log.error(ex.getMessage(), ex);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
#Override
public Response sendError(AuthenticationSessionModel authSession, Error error) {
new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, true);
String redirect = authSession.getRedirectUri();
try {
URI uri = new URI(redirect);
return Response.status(302).location(uri).build();
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
return Response.noContent().build();
}
}
#Override
public void backchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
ClientModel client = clientSession.getClient();
new ResourceAdminManager(session).logoutClientSession(realm, client, clientSession);
}
#Override
public Response frontchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
throw new RuntimeException("NOT IMPLEMENTED");
}
#Override
public Response finishLogout(UserSessionModel userSession) {
return Response.noContent().build();
}
#Override
public boolean requireReauthentication(UserSessionModel userSession, AuthenticationSessionModel authSession) {
return false;
}
#Override
public boolean sendPushRevocationPolicyRequest(RealmModel realm, ClientModel resource, int notBefore, String managementUrl) {
PushNotBeforeAction adminAction = new PushNotBeforeAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, resource.getClientId(), notBefore);
String token = session.tokens().encode(adminAction);
log.tracev("pushRevocation resource: {0} url: {1}", resource.getClientId(), managementUrl);
URI target = UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_PUSH_NOT_BEFORE).build();
try {
int status = session.getProvider(HttpClientProvider.class).postText(target.toString(), token);
boolean success = status == 204 || status == 200;
log.tracef("pushRevocation success for %s: %s", managementUrl, success);
return success;
} catch (IOException e) {
ServicesLogger.LOGGER.failedToSendRevocation(e);
return false;
}
}
#Override
public void close() {
}
}
XyzLoginProtocolService
public class XyzLoginProtocolService {
private final RealmModel realm;
private final EventBuilder event;
#Context
private KeycloakSession session;
#Context
private HttpHeaders headers;
#Context
private HttpRequest request;
#Context
private ClientConnection clientConnection;
public XyzLoginProtocolService(RealmModel realm, EventBuilder event) {
this.realm = realm;
this.event = event;
this.event.realm(realm);
}
#POST
#Path("request")
#Produces(MediaType.APPLICATION_JSON)
#NoCache
public Response request(ApipmLoginRequest loginRequest) {
....
}
I have nested RecyclerView and two LiveData. one is parentList and another one is childList
I managed to use LiveData for ParentAdapter but when I try LiveData for ChildAdapter nothing showen in childAdapter. ParentAdapter is working.
Can someone help me?
Thanks?
this method is in MainActivity.class
private void sendAllDataToAdapter(){
CashFlowViewModel viewModel = ViewModelProviders.of(this).get(CashFlowViewModel.class);
viewModel.cashGroupByDate().observe(this, new Observer<List<CashFlow>>() {
#Override
public void onChanged(List<CashFlow> cashFlows) {
adapter.submitList(cashFlows);
}
});
adapter = new MainAdapter(this, this);
recyclerView.setAdapter(adapter);
}
This is ParentAdapter
public class MainAdapter extends ListAdapter<CashFlow, MainAdapter.MainViewHolder>{
Context context;
List<CashFlow> cashFlowList = new ArrayList<>();
List<CashFlow> cashFlowListChild = new ArrayList<>();
CashflowRepository repository;
CashFlowViewModel viewModel;
LifecycleOwner lifecycleOwner;
public MainAdapter(Context context, LifecycleOwner lifecycleOwner) {
super(diffCallback);
this.context = context;
this.cashFlowList = cashFlowList;
this.cashFlowListChild = cashFlowListChild;
this.repository = repository;
this.lifecycleOwner = lifecycleOwner;
viewModel = ViewModelProviders.of((MainActivity) context).get(CashFlowViewModel.class);
}
private static final DiffUtil.ItemCallback<CashFlow> diffCallback = new DiffUtil.ItemCallback<CashFlow>() {
#Override
public boolean areItemsTheSame(#NonNull CashFlow oldItem, #NonNull CashFlow newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull CashFlow oldItem, #NonNull CashFlow newItem) {
return oldItem.getAdded_date().equals(newItem.getAdded_date())
&& oldItem.getTitle().equals(newItem.getTitle())
&& oldItem.getBody().equals(newItem.getBody());
}
};
#NonNull
#Override
public MainViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.main_adapter, parent, false);
return new MainViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MainViewHolder holder, int position) {
holder.tvDate.setText(getItem(position).getAdded_date());
holder.tvIncome.setText(String.valueOf(getItem(position).getIncome()));
holder.tvExpense.setText(String.valueOf(getItem(position).getExpense()));
ChildAdapter adapter = new ChildAdapter(context);
holder.rvChild.setAdapter(adapter);
viewModel.cashGroupByDate().observe(lifecycleOwner, new Observer<List<CashFlow>>() {
#Override
public void onChanged(List<CashFlow> cashFlows) {
adapter.submitList(cashFlows);
}
});
Log.d("Child", getItem(position).getAdded_date()+"");
}
public class MainViewHolder extends RecyclerView.ViewHolder {
TextView tvDate, tvIncome, tvExpense;
RecyclerView rvChild;
public MainViewHolder(#NonNull View itemView) {
super(itemView);
tvDate = itemView.findViewById(R.id.main_adapter_date);
tvIncome = itemView.findViewById(R.id.main_adapter_income);
tvExpense = itemView.findViewById(R.id.main_adapter_expense);
rvChild = itemView.findViewById(R.id.child_recyclerview);
}
}
This is ChildAdapter
public class ChildAdapter extends ListAdapter<CashFlow, ChildAdapter.ChildViewHolder> {
Context context;
public ChildAdapter(Context context) {
super(diffCallback);
this.context = context;
}
private static final DiffUtil.ItemCallback<CashFlow> diffCallback = new DiffUtil.ItemCallback<CashFlow>() {
#Override
public boolean areItemsTheSame(#NonNull CashFlow oldItem, #NonNull CashFlow newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull CashFlow oldItem, #NonNull CashFlow newItem) {
return oldItem.getAdded_date().equals(newItem.getAdded_date())
&& oldItem.getBody().equals(newItem.getBody())
&& oldItem.getTitle().equals(newItem.getTitle())
&& oldItem.getExpense() == newItem.getExpense()
&& oldItem.getIncome() == newItem.getIncome()
&& oldItem.getType().equals(newItem.getType());
}
};
#NonNull
#Override
public ChildViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.child_adapter, parent, false);
return new ChildViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ChildViewHolder holder, int position) {
holder.imageView.setImageResource(getItem(position).getImage_id());
holder.tvTitle.setText(getItem(position).getTitle());
if (getItem(position).getType().equals(BaseActivity.INCOME)){
holder.tvSum.setText(String.valueOf(getItem(position).getIncome()));
}
else if (getItem(position).getType().equals(BaseActivity.EXPENSE)){
holder.tvSum.setText(String.valueOf(getItem(position).getExpense()));
}
}
public class ChildViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView tvTitle, tvSum;
public ChildViewHolder(#NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.child_adapter_image);
tvTitle = itemView.findViewById(R.id.child_adapter_title);
tvSum = itemView.findViewById(R.id.child_adapter_sum);
}
}
}
This is my ViewModel.class
public class CashFlowViewModel extends AndroidViewModel {
private CashflowRepository repository;
public CashFlowViewModel(#NonNull Application application) {
super(application);
repository = new CashflowRepository(application);
}
public void insert(CashFlow cashFlow){
repository.insert(cashFlow);
}
public void update(CashFlow cashFlow){
repository.update(cashFlow);
}
public void delete(CashFlow cashFlow){
repository.delete(cashFlow);
}
public LiveData<List<CashFlow>> cashGroupByDate(){
return repository.getCashGroupByDate();
}
public LiveData<List<CashFlow>> cashByDate(String addedDate){
return repository.getCashByDate(addedDate);
}
public void insertCategory(Category category){
repository.insertCategory(category);
}
public void updateCategory(Category category){
repository.updateCategory(category);
}
public void deleteCategory(Category category){
repository.deleteCategory(category);
}
public List<Category> allCategories(String type){
return repository.getAllCategories(type);
}
I am using the clean architecture with MVVM pattern so the room part goes into the data layer and I'm returning observables from there to domain layer and using them in presentation layer by wrapping them in a LiveData.
Now, the problem is that after insertion/deletion/update the list is not getting updated immediately in UI.
Viewmodel in Presentation Layer:
public class WordViewModel extends BaseViewModel<WordNavigator> {
//get all the use cases here
private GetAllWords getAllWords;
private InsertWord insertWord;
private DeleteThisWord deleteThisWord;
private UpdateThisWord updateThisWord;
private GetTheIndexOfTopWord getTheIndexOfTopWord;
//data
public MutableLiveData<List<Word>> allWords;
public WordViewModel(GetAllWords getAllWords, InsertWord insertWord, DeleteThisWord deleteThisWord, UpdateThisWord updateThisWord, GetTheIndexOfTopWord getTheIndexOfTopWord) {
this.getAllWords = getAllWords;
this.insertWord = insertWord;
this.deleteThisWord = deleteThisWord;
this.updateThisWord = updateThisWord;
this.getTheIndexOfTopWord = getTheIndexOfTopWord;
}
public void getAllWords() {
getAllWords.execute(new DisposableObserver<List<Word>>() {
#Override
public void onNext(List<Word> words) {
allWords.setValue(words);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
}, GetAllWords.Params.getAllWords());
}
public void insertWord(Word word) {
insertWord.execute(new DisposableObserver<Boolean>() {
#Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word inserted successfully!!!");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
}, InsertWord.Params.insertWord(word));
}
public void getTheIndexOfTopWord(final String action) {
getTheIndexOfTopWord.execute(new DisposableObserver<Word>() {
#Override
public void onNext(Word word) {
if (word != null)
getNavigator().updateTopIndex(word.getWordId(), action);
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
}, GetTheIndexOfTopWord.Params.getTheIndexOfTopWord());
}
public void deleteThisWord(int wordId) {
deleteThisWord.execute(new DisposableObserver<Boolean>() {
#Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word deleted successfully!!!");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
}, DeleteThisWord.Params.deleteThisWord(wordId));
}
public void updateThisWord(int wordId, String newWord) {
updateThisWord.execute(new DisposableObserver<Boolean>() {
#Override
public void onNext(Boolean aBoolean) {
if (aBoolean)
Log.e("ganesh", "word updated successfully!!!");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
}, UpdateThisWord.Params.updateThisWord(wordId, newWord));
}
public MutableLiveData<List<Word>> getWords() {
if (allWords == null) {
allWords = new MutableLiveData<>();
}
return allWords;
}
#Override
protected void onCleared() {
super.onCleared();
if (getAllWords != null)
getAllWords = null;
if (deleteThisWord != null)
deleteThisWord = null;
if (insertWord != null)
insertWord = null;
if (updateThisWord != null)
updateThisWord = null;
if (getTheIndexOfTopWord != null)
getTheIndexOfTopWord = null;
}
}
DAO in Data Layer:
#Dao
public interface WordDao {
#Insert
void insertThisWord(Word word);
#Query("delete from word_table")
void deleteAll();
#Query("select * from word_table order by word_id asc")
List<Word> getAllWords();
#Query("delete from word_table where word_id = :wordId")
void deleteThisWord(int wordId);
#Query("update word_table set word = :newWord where word_id = :wordId")
void updateThisWord(int wordId, String newWord);
#Query("select * from word_table order by word_id asc limit 1")
Word getTheIndexOfTopWord();
}
Repository in Data Layer:
public class WordRepositoryImpl implements WordRepository {
private ApiInterface apiInterface;
private SharedPreferenceHelper sharedPreferenceHelper;
private Context context;
private WordDao wordDao;
private WordRoomDatabase db;
public WordRepositoryImpl(ApiInterface apiInterface, SharedPreferenceHelper sharedPreferenceHelper, WordRoomDatabase db, Context context) {
this.apiInterface = apiInterface;
this.sharedPreferenceHelper = sharedPreferenceHelper;
this.context = context;
this.db = db;
wordDao = db.wordDao();
}
#Override
public Observable<Integer> sum(final int a, final int b) {
return Observable.fromCallable(new Callable<Integer>() {
#Override
public Integer call() throws Exception {
return (a + b);
}
});
}
#Override
public Observable<List<Word>> getAllWords() {
return Observable.fromCallable(new Callable<List<Word>>() {
#Override
public List<Word> call() throws Exception {
List<Word> list = new ArrayList<>();
List<com.example.data.models.Word> listWords = db.wordDao().getAllWords();
for (com.example.data.models.Word item : listWords) {
list.add(new Word(item.getWordId(), item.getWord(), item.getWordLength()));
}
return list;
}
});
}
#Override
public Observable<Boolean> insertThisWord(final Word word) {
return Observable.fromCallable(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
com.example.data.models.Word item = new com.example.data.models.Word(word.getWord(), word.getWordLength());
db.wordDao().insertThisWord(item);
return true;
}
});
}
#Override
public Observable<Boolean> deleteThisWord(final int wordId) {
return Observable.fromCallable(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
db.wordDao().deleteThisWord(wordId);
return true;
}
});
}
#Override
public Observable<Boolean> updateThisWord(final int wordId, final String newWord) {
return Observable.fromCallable(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
db.wordDao().updateThisWord(wordId, newWord);
return true;
}
});
}
#Override
public Observable<Word> getTheIndexOfTopWord() {
return Observable.fromCallable(new Callable<Word>() {
#Override
public Word call() throws Exception {
com.example.data.models.Word item = db.wordDao().getTheIndexOfTopWord();
Word word = new Word(item.getWordId(), item.getWord(), item.getWordLength());
return word;
}
});
}
}
GetAllWordsUseCase in Domain Layer:
public class GetAllWords extends UseCase<List<Word>, GetAllWords.Params> {
private WordRepository wordRepository;
public GetAllWords(PostExecutionThread postExecutionThread, WordRepository wordRepository) {
super(postExecutionThread);
this.wordRepository = wordRepository;
}
#Override
public Observable<List<Word>> buildUseCaseObservable(Params params) {
return wordRepository.getAllWords();
}
public static final class Params {
private Params() {
}
public static GetAllWords.Params getAllWords() {
return new GetAllWords.Params();
}
}
}
UseCase Base Class in domain layer:
public abstract class UseCase<T, Params> {
private final PostExecutionThread postExecutionThread;
private final CompositeDisposable compositeDisposable;
public UseCase(PostExecutionThread postExecutionThread) {
this.postExecutionThread = postExecutionThread;
this.compositeDisposable = new CompositeDisposable();
}
/**
* Builds an {#link Observable} which will be used when executing the current {#link UseCase}.
*/
public abstract Observable<T> buildUseCaseObservable(Params params);
/**
* Dispose from current {#link CompositeDisposable}.
*/
public void dispose() {
if (!compositeDisposable.isDisposed()) {
compositeDisposable.dispose();
}
}
/**
* Executes the current use case.
*
* #param observer {#link DisposableObserver} which will be listening to the observable build
* by {#link #buildUseCaseObservable(Params)} ()} method.
* #param params Parameters (Optional) used to build/execute this use case.
*/
public void execute(DisposableObserver<T> observer, Params params) {
if (observer != null) {
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.io())
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
}
/**
* Dispose from current {#link CompositeDisposable}.
*/
private void addDisposable(Disposable disposable) {
if (disposable != null && compositeDisposable != null)
compositeDisposable.add(disposable);
}
}
Finally, WordActivity in Presentation Layer
public class WordActivity extends BaseActivity<WordViewModel> implements
View.OnClickListener, WordNavigator {
#Inject
WordViewModel wordViewModel;
private Button deleteButton, updateButton, addButton;
private EditText editTextWord;
private WordListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_word);
((MainApplication) getApplicationContext()).getComponent().inject(this);
editTextWord = findViewById(R.id.activity_word_et_word);
deleteButton = findViewById(R.id.activity_main_delete_button);
updateButton = findViewById(R.id.activity_main_update_button);
addButton = findViewById(R.id.activity_word_btn_add_word);
deleteButton.setOnClickListener(this);
updateButton.setOnClickListener(this);
addButton.setOnClickListener(this);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
adapter = new WordListAdapter(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
getViewModel().setNavigator(this);
getViewModel().getAllWords();
getViewModel().getWords().observe(this, new Observer<List<Word>>() {
#Override
public void onChanged(#android.support.annotation.Nullable List<Word> words) {
adapter.setWords(words);
}
});
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.activity_main_delete_button:
getViewModel().getTheIndexOfTopWord(TOP_INDEX_ACTION_DELETE);
break;
case R.id.activity_main_update_button:
getViewModel().getTheIndexOfTopWord(TOP_INDEX_ACTION_UPDATE);
break;
case R.id.activity_word_btn_add_word:
handleAddButtonClick();
break;
}
}
public void handleAddButtonClick() {
String text = editTextWord.getText().toString();
if (text.equals("")) {
Toast.makeText(getApplicationContext(), R.string.empty_not_saved, Toast.LENGTH_LONG).show();
} else {
Word word = new Word(text, text.length());
getViewModel().insertWord(word);
editTextWord.setText("");
editTextWord.clearFocus();
}
}
#Override
public void updateTopIndex(Integer wordId, String action) {
if (action.equals(TOP_INDEX_ACTION_DELETE))
getViewModel().deleteThisWord(wordId);
else
getViewModel().updateThisWord(wordId, "dsakagdad");
}
#Override
public WordViewModel getViewModel() {
return wordViewModel;
}
}
**getViewModel().getWords().observe(this, new Observer<List<Word>>() {
#Override
public void onChanged(#android.support.annotation.Nullable List<Word> words) {
adapter.setWords(words);
}
});**
//This portion is getting called only once but not when I
insert/update/delete words from room database!
Can anyone go through these and help me out here!
This method in the DAO will query for the list and return it like a normal SQL query:
#Query("select * from word_table order by word_id asc")
List<Word> getAllWords();
If you want to observe for changes you might wanna consider using an RxJava2 Flowable/Observable or a LiveData for that.
As I prefer the RxJava approach, It will look like this:
#Query("select * from word_table order by word_id asc")
Flowable<List<Word>> getAllWords();
// or
Observable<List<Word>> getAllWords();
Difference between Flowable and Observable
With that done, you might wanna change the getAllWords method in the repository to return that Flowable/Observable.
Note: Either using an Observable or a Flowable both will emit the query result initially once and then start observing for further changes till unsubscribed to.
Read more about Room with RxJava
I want to fire the "open root node" event on my current working CellTree, which now has the following behaviour:
#Override
public <T> NodeInfo<?> getNodeInfo(final T value) {
return new DefaultNodeInfo<Categoria>(
(value instanceof Categoria) ?
createBranchDataProvider((Categoria)value) :
rootDataProvider,
new CategoriaCell()
);
}
private AsyncDataProvider<Categoria> createRootDataProvider() {
AsyncDataProvider<Categoria> dataProvider = new AsyncDataProvider<Categoria>() {
#Override
protected void onRangeChanged(HasData<Categoria> display) {
AsyncCallback<Categoria[]> cb = new AsyncCallback<Categoria[]>() {
#Override
public void onSuccess(Categoria[] result) {
updateRowCount(result.length, true);
updateRowData(0, Arrays.asList(result));
}
#Override
public void onFailure(Throwable caught) {
Window.alert(caught.toString());
}
};
rpcService.getCategorie(cb);
}
};
return dataProvider;
}
How can I fire that "onRangeChanged" event, to refresh my level-1 nodes?
What is my convenience method missing?
private void updateTree() {
TreeNode rootTreeNode = cellTree.getRootTreeNode();
for (int i = 0; i < rootTreeNode.getChildCount(); i++) {
rootTreeNode.setChildOpen(i, false);
}
// HOW TO REFRESH LEVEL-1 NODES?
}
Working example. Add reference to DataProvider (and parent Node) (MyMenuItem and MyCell with DataProvider in my code). After adding element refresh parent.
public class MyMenuItem {
private String name;
private String action; //some data
private int level; //if needed
private ArrayList<MyMenuItem> list; //nodes childrens
private MyMenuItem parent; //track internal parent
private MyCell cell; //for refresh - reference to visual component
public void setCell(MyCell cell) {
this.cell = cell;
}
public void refresh() {
if(parent!=null) {
parent.refresh();
}
if (cell!=null) {
cell.refresh(); //refresh tree
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public MyMenuItem(String name, String action) {
super();
parent = null;
level = 0;
this.name = name;
this.action = action;
list = new ArrayList<MyMenuItem>();
}
public MyMenuItem(String name) {
this(name, "");
}
public void addSubMenu(MyMenuItem m) {
m.level = this.level+1;
m.parent = this;
list.add(m);
}
public boolean hasChildrens() {
return list.size()>0;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public ArrayList<MyMenuItem> getList() {
return list;
}
public MyMenuItem getParent() {
return parent;
}
}
public class MyTreeModel implements TreeViewModel {
private MyMenuItem officialRoot; //default not dynamic
private MyMenuItem studentRoot; //default not dynamic
private MyMenuItem testRoot; //default not dynamic
private MyMenuItem root;
public MyMenuItem getRoot() { // to set CellTree root
return root;
}
public MyTreeModel() {
root = new MyMenuItem("root");
// Default items
officialRoot = new MyMenuItem("Official"); //some basic static data
studentRoot = new MyMenuItem("Student");
testRoot = new MyMenuItem("Test");
root.addSubMenu(officialRoot);
root.addSubMenu(studentRoot);
root.addSubMenu(testRoot);
}
//example of add add logic
private void addNew(MyMenuItem myparent, String name, String uid) {
myparent.addSubMenu(new MyMenuItem(name, uid));
myparent.refresh(); //HERE refresh tree
}
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
ListDataProvider<MyMenuItem> dataProvider;
MyMenuItem myValue = null;
if (value == null) { // root is not set
dataProvider = new ListDataProvider<MyMenuItem>(root.getList());
} else {
myValue = (MyMenuItem) value;
dataProvider = new ListDataProvider<MyMenuItem>(myValue.getList());
}
MyCell cell = new MyCell(dataProvider); //HERE Add reference
if (myValue != null)
myValue.setCell(cell);
return new DefaultNodeInfo<MyMenuItem>(dataProvider, cell);
}
#Override
public boolean isLeaf(Object value) {
if (value instanceof MyMenuItem) {
MyMenuItem t = (MyMenuItem) value;
if (!t.hasChildrens())
return true;
return false;
}
return false;
}
}
public class MyCell extends AbstractCell<MyMenuItem> {
ListDataProvider<MyMenuItem> dataProvider; //for refresh
public MyCell(ListDataProvider<MyMenuItem> dataProvider) {
super("keydown","dblclick");
this.dataProvider = dataProvider;
}
public void refresh() {
dataProvider.refresh();
}
#Override
public void onBrowserEvent(Context context, Element parent, MyMenuItem value,
NativeEvent event, ValueUpdater<MyMenuItem> valueUpdater) {
if (value == null) {
return;
}
super.onBrowserEvent(context, parent, value, event, valueUpdater);
if ("click".equals(event.getType())) {
this.onEnterKeyDown(context, parent, value, event, valueUpdater);
}
if ("dblclick".equals(event.getType())) {
this.onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
#Override
public void render(Context context, MyMenuItem value, SafeHtmlBuilder sb) {
if (value == null) {
return;
}
sb.appendEscaped(value.getName());
//add HERE for better formating
}
#Override
protected void onEnterKeyDown(Context context, Element parent,
MyMenuItem value, NativeEvent event, ValueUpdater<MyMenuItem> valueUpdater) {
Window.alert("You clicked "+event.getType()+" " + value.getName());
}
}
in module add
treeModel = new MyTreeModel();
tree = new CellTree(treeModel,treeModel.getRoot());
The Level-1 nodes (I suppose you the mean below the root node) can not be refreshed the way you are doing it.
You have to store the instance of your dataProvider for the level-1 nodes somewhere.
Later when you refresh your list you have to update your stored dataProvider for your level-1 nodes.
The nodes below the level-1 can be refreshed the way you are doing it. Because as soon as you close the level 1 nodes (that is what you are doing in the updateTree method) and the next time you open it getNodeInfo will be called and the updated Subcategories will be retrieved and displayed in the CellTree.
UPDATE
For refreshing the CellWidgets which is attached to AsyncDataProvider you will probably have to extend the AsyncDataProvider and either extract the RPC call to a getData() method which is called in the onRangeChanged() method or create an interface with a refresh method and implement it in your custom AsyncDataProvider which calls the protected onRangeChanged() method.
I tried following the example, http://gwt.google.com/samples/Showcase/Showcase.html#!CwCellTree , and added two ActionCells inside of the CompositeCell with no luck. The ActionCell's onBrowserEvent() does not get triggered.
This simple example works for me. Since you didn't provide any code or further explanation on what exactly you're trying to achieve, I have no idea whether my example is of any help or not.
public void onModuleLoad() {
CellTable<Person> table = new CellTable<Starter.Person>();
List<HasCell<Person, ?>> cells = new LinkedList<HasCell<Person, ?>>();
cells.add(new HasCellImpl("first name", new Delegate<Person>() {
#Override
public void execute(Person object) {
Window.alert(object.getFirstName());
}
}));
cells.add(new HasCellImpl("last name", new Delegate<Starter.Person>() {
#Override
public void execute(Person object) {
Window.alert(object.getLastName());
}
}));
CompositeCell<Person> cell = new CompositeCell<Person>(cells);
table.addColumn(new TextColumn<Starter.Person>() {
#Override
public String getValue(Person object) {
return object.getFirstName() + " " + object.getLastName();
}
}, "name");
table.addColumn(new Column<Person, Person>(cell) {
#Override
public Person getValue(Person object) {
return object;
}
}, "composite");
LinkedList<Person> data = new LinkedList<Starter.Person>();
data.add(new Person("Amy", "Reed"));
data.add(new Person("Tim", "Gardner"));
table.setRowData(data);
RootPanel.get().add(table);
}
private class HasCellImpl implements HasCell<Person, Person> {
private ActionCell<Person> fCell;
public HasCellImpl(String text, Delegate<Person> delegate) {
fCell = new ActionCell<Person>(text, delegate);
}
#Override
public Cell<Person> getCell() {
return fCell;
}
#Override
public FieldUpdater<Person, Person> getFieldUpdater() {
return null;
}
#Override
public Person getValue(Person object) {
return object;
}
}
private class Person {
private String fFirstName;
private String fLastName;
public Person(String first, String last) {
fFirstName = first;
fLastName = last;
}
public String getFirstName() {
return fFirstName;
}
public String getLastName() {
return fLastName;
}
}