Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ default void upsert(DbLight obj) {
// Case 3
// Primary key NOT found in light object. This can happen if (all) light objects are retrieved from the
// remote endpoint.
// Rhe lightId primary key is autogenerated by room, therefore it is not known to the remote endpoint.
// The lightId primary key is autogenerated by room, therefore it is not known to the remote endpoint.
else if (obj.getEndpointId() != 0L && !(obj.getEndpointLightId().equals(""))) {
int rowsUpdated =
updateUsingEndpointIdAndEndpointLightId(obj.getEndpointId(), obj.getEndpointLightId(), obj.getName(),
obj.getIsSwitchable(), obj.getIsOn(), obj.getIsDimmable(), obj.getBrightness(),
obj.getIsTemperaturable(), obj.getColorTemperature(), obj.getIsColorable(), obj.getColor());
obj.getIsTemperaturable(), obj.getColorTemperature(), obj.getIsColorable(), obj.getColor(),
obj.getIsReachable());
Log.d(TAG,
rowsUpdated + " rows updated by room. Updated DbLight with endpointId: " + obj.getEndpointId() +
" and endpointLightId: " + obj.getEndpointLightId());
Expand Down Expand Up @@ -100,12 +101,12 @@ else if (obj.getEndpointId() != 0L && !(obj.getEndpointLightId().equals(""))) {
*/
@Query("UPDATE " + DbLight.TABLENAME + " SET name = :friendlyName, is_switchable = :switchable, is_on = :on, " +
"is_dimmable = :dimmable, brightness = :brightness, is_temperaturable = :temperaturable, " +
"colortemperature = :colorTemperature, is_colorable = :colorable, color = :color WHERE endpoint_id = " +
":endpointId AND endpoint_light_id = :endpointLightId")
"colortemperature = :colorTemperature, is_colorable = :colorable, color = :color, is_reachable = " +
":is_reachable WHERE endpoint_id = " + ":endpointId AND endpoint_light_id = :endpointLightId")
int updateUsingEndpointIdAndEndpointLightId(long endpointId, String endpointLightId, String friendlyName,
boolean switchable, boolean on, boolean dimmable, int brightness,
boolean temperaturable, int colorTemperature, boolean colorable,
int color);
int color, boolean is_reachable);

@Delete()
void delete(DbLight obj);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ public class DbLightBuilder {
/**
* Builder for constructing DbLights.
*/

public static DbLightBuilder from(DbLight dbLight) {
DbLightBuilder builder = new DbLightBuilder();
builder.setEndpointId(dbLight.getEndpointId());
builder.setEndpointLightId(dbLight.getEndpointLightId());
builder.setName(dbLight.getName());
builder.setIsSwitchable(dbLight.getIsSwitchable());
builder.setIsOn(dbLight.getIsOn());
builder.setIsDimmable(dbLight.getIsDimmable());
builder.setBrightness(dbLight.getBrightness());
builder.setIsTemperaturable(dbLight.getIsTemperaturable());
builder.setColorTemperature(dbLight.getColorTemperature());
builder.setIsColorable(dbLight.getIsColorable());
builder.setColor(dbLight.getColor());
builder.setIsReachable(dbLight.getIsReachable());
return builder;
}

public DbLightBuilder() {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ private UILight(long lightId, long endpointId, String name, boolean isSwitchable
@NonNull
@Contract("_ -> new")
public static UILight from(@NonNull DbLight dbLight) {
Log.d(TAG, "Converting DbLight to UiLight...");
// Place for conversion logic (if UI needs other data types or value ranges).
UILight uiLight =
new UILight(dbLight.getLightId(), dbLight.getEndpointId(), dbLight.getName(), dbLight.getIsSwitchable(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.d3kad3nt.sunriseClock.data.repository;

import android.content.Context;
import android.util.Log;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -163,6 +164,8 @@ protected UILight convertDbTypeToResultType(DbLight item) {

@Override
protected DbLight convertRemoteTypeToDbType(ApiSuccessResponse<RemoteLight> response) {
Log.d(TAG, "Convert: Light " + response.getBody().getName() + " is reachable: " +
response.getBody().getIsReachable());
return DbLight.from(response.getBody());
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@

/**
* A generic class that can provide a resource backed by both the sqlite database and the network. Copied from the
* official Google architecture-components github-sample under https://github
* .com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android
* /example/github/repository/NetworkBoundResource.kt
* official Google architecture-components github-sample under <a
* href="https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main
* /java/com/android/example/github/repository/NetworkBoundResource.kt">NetworkBoundResource Source</a>
*
* You can read more about it in the [Architecture Guide](https://developer.android.com/arch).
* You can read more about it in the <a href="https://developer.android.com/arch">Architecture Guide</a>
*/
public abstract class NetworkBoundResource <ResultType, RemoteType, DbType> extends ExtendedMediatorLiveData<Resource<ResultType>> {

Expand All @@ -60,6 +60,9 @@ private void dbSourceObserver(DbType data, LiveData<DbType> dbSourceLiveData) {
dbObject = data;
removeSource(dbSourceLiveData);
if (shouldFetch(dbObject)) {
addSource(dbSource, newData -> {
updateValue(Resource.success(convertDbTypeToResultType(newData)));
});
LiveData<BaseEndpoint> endpointLiveData = loadEndpoint();
addSource(endpointLiveData, baseEndpoint -> {
endpointLiveDataObserver(baseEndpoint, endpointLiveData);
Expand All @@ -73,9 +76,7 @@ private void dbSourceObserver(DbType data, LiveData<DbType> dbSourceLiveData) {
}

private void endpointLiveDataObserver(BaseEndpoint endpoint, LiveData<BaseEndpoint> endpointLiveData) {
if (endpoint == null) {
updateValue(Resource.loading(null));
} else {
if (endpoint != null) {
this.endpoint = endpoint;
fetchFromNetwork(dbSource);
removeSource(endpointLiveData);
Expand All @@ -85,14 +86,10 @@ private void endpointLiveDataObserver(BaseEndpoint endpoint, LiveData<BaseEndpoi
private void fetchFromNetwork(LiveData<DbType> dbSource) {
LiveData<ApiResponse<RemoteType>> apiResponse = loadFromNetwork();
// we re-attach dbSource as a new source, it will dispatch its latest value quickly
addSource(dbSource, newData -> {
updateValue(Resource.loading(convertDbTypeToResultType(newData)));
});
addSource(apiResponse, response -> {
removeSource(apiResponse);
removeSource(dbSource);
Class<? extends ApiResponse> aClass = response.getClass();
if (ApiSuccessResponse.class.equals(aClass)) {
if (response instanceof ApiSuccessResponse) {
ServiceLocator.getExecutor(ExecutorType.IO).execute(() -> {
saveResponseToDb(convertRemoteTypeToDbType((ApiSuccessResponse<RemoteType>) response));
ServiceLocator.getExecutor(ExecutorType.MainThread).execute(() -> {
Expand All @@ -105,14 +102,14 @@ private void fetchFromNetwork(LiveData<DbType> dbSource) {
});
});
});
} else if (ApiEmptyResponse.class.equals(aClass)) {
} else if (response instanceof ApiEmptyResponse) {
ServiceLocator.getExecutor(ExecutorType.MainThread).execute(() -> {
// reload from disk whatever we had
addSource(loadFromDb(), newData -> {
updateValue(Resource.success(convertDbTypeToResultType(newData)));
});
});
} else if (ApiErrorResponse.class.equals(aClass)) {
} else if (response instanceof ApiErrorResponse) {
onFetchFailed();
addSource(dbSource, newData -> {
updateValue(Resource.error(((ApiErrorResponse<RemoteType>) response).getErrorMessage(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.d3kad3nt.sunriseClock.data.model.resource.Status;
import org.d3kad3nt.sunriseClock.data.repository.EndpointRepository;
import org.d3kad3nt.sunriseClock.data.repository.LightRepository;
import org.d3kad3nt.sunriseClock.util.Action;
import org.d3kad3nt.sunriseClock.util.AsyncJoin;
import org.d3kad3nt.sunriseClock.util.ExtendedPublisher;
import org.d3kad3nt.sunriseClock.util.LiveDataUtil;
Expand Down Expand Up @@ -77,7 +78,7 @@ public Flow.Publisher<Control> createPublisherForAllAvailable() {
LiveData<List<IEndpointUI>> allEndpoints = getEndpointRepository().getAllEndpoints();
AsyncJoin asyncHelper = new AsyncJoin();

LiveDataUtil.observeOnce(allEndpoints, new AsyncJoin.Observer<>(asyncHelper) {
allEndpoints.observeForever(new AsyncJoin.Observer<>(asyncHelper) {
@Override
public void onChanged(final List<IEndpointUI> endpoints) {
for (IEndpointUI endpoint : endpoints) {
Expand Down Expand Up @@ -106,6 +107,7 @@ public void onChanged(final Resource<List<UILight>> listResource) {
});
}
asyncHelper.removeAsyncTask(this);
allEndpoints.removeObserver(this);
}
});
asyncHelper.executeWhenJoined(() -> flow.complete());
Expand Down Expand Up @@ -308,9 +310,9 @@ private Context getNonNullBaseContext() {
}

private void initEndpointNames() {
LiveDataUtil.observeOnce(getEndpointRepository().getAllEndpoints(), new Observer<>() {
LiveDataUtil.observeOnce(getEndpointRepository().getAllEndpoints(), new Action<>() {
@Override
public void onChanged(final List<IEndpointUI> iEndpointUIS) {
public void execute(final List<IEndpointUI> iEndpointUIS) {
for (IEndpointUI endpoint : iEndpointUIS) {
endpointNames.put(endpoint.getId(), endpoint.getStringRepresentation());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.d3kad3nt.sunriseClock.ui.light;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
Expand All @@ -19,6 +20,8 @@
import org.d3kad3nt.sunriseClock.data.model.resource.Resource;
import org.d3kad3nt.sunriseClock.data.model.resource.Status;
import org.d3kad3nt.sunriseClock.databinding.LightsFragmentBinding;
import org.d3kad3nt.sunriseClock.ui.MainActivity;
import org.jetbrains.annotations.Contract;

import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -46,8 +49,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
@Override
public void onChanged(Resource<List<UILight>> listResource) {
Log.d(TAG, listResource.getStatus().toString());
if (listResource.getStatus().equals(Status.SUCCESS) && listResource.getData() != null) {
lightsState.clearError();
if (listResource.getData() != null){
List<UILight> list = listResource.getData();
Collections.sort(list, new Comparator<>() {
@Override
Expand All @@ -56,11 +58,17 @@ public int compare(final UILight uiLight, final UILight uiLight2) {
}
});
adapter.submitList(list);
} else if (listResource.getStatus().equals(Status.ERROR)) {
lightsState.setError(getResources().getString(R.string.noLights_title),
listResource.getMessage());
} else {
adapter.submitList(List.of());
}
switch (listResource.getStatus()){
case SUCCESS:
lightsState.clearError();
break;
case ERROR:
String errorMsg = getResources().getString(R.string.noLights_title);
lightsState.setError(errorMsg, listResource.getMessage());
}
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.d3kad3nt.sunriseClock.ui.util.BooleanVisibilityLiveData;
import org.d3kad3nt.sunriseClock.ui.util.ResourceVisibilityLiveData;
import org.d3kad3nt.sunriseClock.util.LiveDataUtil;
import org.jetbrains.annotations.Contract;

import kotlin.jvm.functions.Function1;

Expand Down Expand Up @@ -69,14 +70,19 @@ private LiveData<Resource<UILight>> getLight(long lightID) {
return lightRepository.getLight(lightID);
}

@NonNull
@Contract(" -> new")
private LiveData<Boolean> getIsReachable() {
return Transformations.map(light, new Function1<>() {
@Override
public Boolean invoke(final Resource<UILight> input) {
if (input.getStatus() == Status.SUCCESS) {
if (input.getStatus() == Status.ERROR) {
return false;
}
if (input.getData() != null) {
return input.getData().getIsReachable();
}
return true;
return false;
}
});
}
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/org/d3kad3nt/sunriseClock/util/Action.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.d3kad3nt.sunriseClock.util;

@FunctionalInterface
public interface Action <T> {

void execute(final T t);
}
20 changes: 17 additions & 3 deletions app/src/main/java/org/d3kad3nt/sunriseClock/util/LiveDataUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;

public class LiveDataUtil {

public static <T> void logChanges(String TAG, LiveData<T> liveData) {
public static <T> void logChanges(final String TAG, @NonNull final LiveData<T> liveData) {
liveData.observeForever(t -> {
Log.d("LiveDataUtil", "Log Change");
Log.d(TAG, t.toString());
});
}

public static <T> void observeOnce(LiveData<T> liveData, Observer<T> observer) {
public static <T> void observeOnce(@NonNull final LiveData<T> liveData, @NonNull final Action<T> action) {
liveData.observeForever(new Observer<T>() {
@Override
public void onChanged(T t) {
observer.onChanged(t);
action.execute(t);
liveData.removeObserver(this);
}
});
}

public static <T> void observeUntilNotNull(@NonNull final LiveData<T> light, @NonNull final Action<T> action) {
light.observeForever(new Observer<T>() {
@Override
public void onChanged(final T t) {
if (t == null) {
return;
}
light.removeObserver(this);
action.execute(t);
}
});
}
}