diff --git a/mobile/build.gradle b/mobile/build.gradle index 0500571d5899be3805174c15ae5059350a870c3e..62b7697066517b842544a9aa47f10b17db1718a2 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -122,9 +122,6 @@ dependencies { exclude group: 'com.intellij', module: 'annotations' } - // MaterialDialogs - implementation 'com.afollestad.material-dialogs:core:2.0.0-beta4' - // Google Maps fullImplementation 'com.google.android.gms:play-services-maps:12.0.1' // GCM diff --git a/mobile/src/main/java/org/openhab/habdroid/model/ChangedItem.java b/mobile/src/main/java/org/openhab/habdroid/model/ChangedItem.java index 61935d965491ec3357ef01c93fbd9c91137a714b..adab499172a48236334b376adb88f87fa1eb30f6 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/ChangedItem.java +++ b/mobile/src/main/java/org/openhab/habdroid/model/ChangedItem.java @@ -15,6 +15,13 @@ public class ChangedItem { @Expose private String state; + private ChangedItemType type; + + public enum ChangedItemType { + ColorLamp, + DimmableLamp, + } + public String getName() { return name; } @@ -38,4 +45,14 @@ public class ChangedItem { public void setState(String state) { this.state = state; } + + public void setType(ChangedItemType type) { + this.type = type; + } + + public ChangedItemType getType() { + if (type == null) + return ChangedItemType.ColorLamp; + return type; + } } diff --git a/mobile/src/main/java/org/openhab/habdroid/model/Recognitions.java b/mobile/src/main/java/org/openhab/habdroid/model/Recognition.java similarity index 96% rename from mobile/src/main/java/org/openhab/habdroid/model/Recognitions.java rename to mobile/src/main/java/org/openhab/habdroid/model/Recognition.java index f2c0c7d5852f3f3b2b1af14f70eacea9407ed09e..f833f8d08b7e0ee66aca1720485f965211cc418e 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/Recognitions.java +++ b/mobile/src/main/java/org/openhab/habdroid/model/Recognition.java @@ -1,5 +1,7 @@ package org.openhab.habdroid.model; +import android.util.Log; + import java.util.List; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; @@ -10,7 +12,7 @@ import com.google.gson.annotations.SerializedName; * By Muhammad Ibrahim Rahman */ -public class Recognitions { +public class Recognition { @SerializedName("identifier") @Expose diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java b/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java index 7fb791e504f1adfac76130c96394e40a4cc421ec..f487538c0a61184207cf3e3d1a6c3ca3a10d3b1a 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/MainActivity.java @@ -79,6 +79,7 @@ import org.openhab.habdroid.core.connection.exception.NoUrlInformationException; import org.openhab.habdroid.model.LinkedPage; import org.openhab.habdroid.model.ServerProperties; import org.openhab.habdroid.model.Sitemap; +import org.openhab.habdroid.model.Widget; import org.openhab.habdroid.ui.activity.ContentController; import org.openhab.habdroid.util.AsyncHttpClient; import org.openhab.habdroid.util.AsyncServiceResolver; @@ -94,6 +95,7 @@ import java.security.cert.CertPathValidatorException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.CertificateRevokedException; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -140,6 +142,8 @@ public class MainActivity extends AppCompatActivity implements private ServerProperties.UpdateHandle mPropsUpdateHandle; private boolean mStarted; + private ArrayList<Widget> mWidgets = new ArrayList<>(); + /** * Daydreaming gets us into a funk when in fullscreen, this allows us to * reset ourselves to fullscreen. @@ -255,6 +259,11 @@ public class MainActivity extends AppCompatActivity implements prefsEditor.apply(); } + public void updateWidgets(List<Widget> widgets) { + mWidgets.clear(); + mWidgets.addAll(widgets); + } + private void handleConnectionChange() { if (mConnection instanceof DemoConnection) { showSnackbar(R.string.info_demo_mode_short); @@ -568,6 +577,7 @@ public class MainActivity extends AppCompatActivity implements return true; case R.id.recognitions: openRecognitions(); + return true; default: break; @@ -690,7 +700,7 @@ public class MainActivity extends AppCompatActivity implements private void openRecognitions() { Intent recognitionsIntent = new Intent(this, RecognitionsActivity.class); recognitionsIntent.putExtra("serverProperties", mServerProperties); - + recognitionsIntent.putParcelableArrayListExtra("widgets", mWidgets); startActivityForResult(recognitionsIntent, INFO_REQUEST_CODE); Util.overridePendingTransition(this, false); } diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/PreferencesActivity.java b/mobile/src/main/java/org/openhab/habdroid/ui/PreferencesActivity.java index 91b0d124a7c9d280f4e77fc71528d2678e5483cd..5548b677ff80571fdc1596e67d3e3e1997fd59f7 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/PreferencesActivity.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/PreferencesActivity.java @@ -25,6 +25,9 @@ import android.preference.PreferenceScreen; import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; +import android.widget.EditText; +import android.widget.Toast; + import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -42,6 +45,8 @@ import org.openhab.habdroid.util.CacheManager; import org.openhab.habdroid.util.Constants; import org.openhab.habdroid.util.Util; +import java.net.MalformedURLException; +import java.net.URL; import java.util.BitSet; import static org.openhab.habdroid.util.Util.getHostFromUrl; @@ -229,6 +234,7 @@ public class PreferencesActivity extends AppCompatActivity { final Preference remoteConnPref = findPreference(Constants.SUBSCREEN_REMOTE_CONNECTION); final Preference themePref = findPreference(Constants.PREFERENCE_THEME); final Preference clearCachePref = findPreference(Constants.PREFERENCE_CLEAR_CACHE); + final Preference openLichtUrl = findPreference(Constants.PREFERENCE_OPENLICHT_URL); final Preference clearDefaultSitemapPref = findPreference(Constants.PREFERENCE_CLEAR_DEFAULT_SITEMAP); final Preference ringtonePref = findPreference(Constants.PREFERENCE_TONE); @@ -244,7 +250,17 @@ public class PreferencesActivity extends AppCompatActivity { clearDefaultSitemapPref.setSummary(getString( R.string.settings_current_default_sitemap, currentDefaultSitemapLabel)); } - + openLichtUrl.setOnPreferenceChangeListener((preference, newValue) -> { + try { + new URL(newValue.toString()).toURI(); + preference.setSummary(newValue.toString()); + return true; + } catch (Exception e) { + Toast.makeText(this.getActivity(), "URL not valid. Don't forget to add http(s):// at the beginning!", Toast.LENGTH_LONG).show(); + return false; + } + }); + updateOpenLichtUrlPreference(openLichtUrl); updateConnectionSummary(Constants.SUBSCREEN_LOCAL_CONNECTION, Constants.PREFERENCE_LOCAL_URL, Constants.PREFERENCE_LOCAL_USERNAME, Constants.PREFERENCE_LOCAL_PASSWORD); @@ -388,6 +404,15 @@ public class PreferencesActivity extends AppCompatActivity { ? R.drawable.ic_smartphone_grey_24dp : R.drawable.ic_vibration_grey_24dp); } + private boolean updateOpenLichtUrlPreference(Preference pref) { + String currentUrl = getPreferenceString(Constants.PREFERENCE_OPENLICHT_URL, ""); + if (!currentUrl.isEmpty()) { + pref.setSummary(currentUrl); + return true; + } + return false; + } + private void updateConnectionSummary(String subscreenPrefKey, String urlPrefKey, String userPrefKey, String passwordPrefKey) { Preference pref = findPreference(subscreenPrefKey); diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsActivity.java b/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsActivity.java index cd222572c8d91f289bcf4a3270e4ce16384f9a3d..8f039c62c42d224dc7c7133e4ee1aaed29d20556 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsActivity.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsActivity.java @@ -1,19 +1,31 @@ package org.openhab.habdroid.ui; import android.app.ProgressDialog; +import android.os.Build; import android.os.Bundle; +import android.util.Log; import android.view.MenuItem; import org.openhab.habdroid.R; +import org.openhab.habdroid.core.connection.Connection; +import org.openhab.habdroid.core.connection.ConnectionFactory; +import org.openhab.habdroid.core.connection.exception.ConnectionException; import org.openhab.habdroid.model.ChangedItem; +import org.openhab.habdroid.model.LinkedPage; +import org.openhab.habdroid.model.Recognition; +import org.openhab.habdroid.model.Widget; import org.openhab.habdroid.util.Util; -import org.openhab.habdroid.model.Recognitions; import org.openhab.habdroid.util.GetDataService; import org.openhab.habdroid.util.RetrofitClientInstance; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; import java.util.List; +import java.util.Locale; -import androidx.cardview.widget.CardView; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; @@ -22,19 +34,24 @@ import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentManager; -import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + +import android.widget.TextView; import android.widget.Toast; /* - * Activity for OpenLicht Recognitions + * Activity for OpenLicht Recognition * By Muhammad Ibrahim Rahman */ -public class RecognitionsActivity extends AppCompatActivity{ +public class RecognitionsActivity extends AppCompatActivity implements WidgetAdapter.ItemClickListener { + + private RecognitionsAdapter mPastRecognitionsAdapter; + private RecyclerView mRvPastRecognitions; + private SwipeRefreshLayout mSwipeRefreshLayout; - private RecognitionsAdapter adapter; - private RecyclerView recyclerView; ProgressDialog progressDialog; @Override @@ -47,6 +64,8 @@ public class RecognitionsActivity extends AppCompatActivity{ Toolbar toolbar = findViewById(R.id.openhab_toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); + mSwipeRefreshLayout = findViewById(R.id.swipeRefresh_recognitions); + mSwipeRefreshLayout.setOnRefreshListener(() -> getRESTData()); if (savedInstanceState == null) { getRESTData(); @@ -59,39 +78,94 @@ public class RecognitionsActivity extends AppCompatActivity{ private void getRESTData() { progressDialog = new ProgressDialog(RecognitionsActivity.this); progressDialog.setMessage("Loading"); - progressDialog.show(); + if (!mSwipeRefreshLayout.isRefreshing()) progressDialog.show(); // Interface for the RetrofitInstance - GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class); - - Call<List<Recognitions>> call = service.getAllRecognitions(); - call.enqueue(new Callback<List<Recognitions>>() { + try { + GetDataService service = RetrofitClientInstance.getRetrofitInstance(this).create(GetDataService.class); + Call<List<Recognition>> call = service.getAllRecognitions(); + call.enqueue(new Callback<List<Recognition>>() { + + @Override + public void onResponse(Call<List<Recognition>> call, Response<List<Recognition>> response) { + + progressDialog.dismiss(); + //generateDataList(response.body()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + response.body().sort((o1, o2) -> o2.getTimestamp() - o1.getTimestamp()); + } + generateCurrentActivity(response.body().get(0)); + generatePastActivities(response.body().subList(1, response.body().size())); + mSwipeRefreshLayout.setRefreshing(false); + } + + @Override + public void onFailure(Call<List<Recognition>> call, Throwable t) { + progressDialog.dismiss(); + mSwipeRefreshLayout.setRefreshing(false); + Toast.makeText(RecognitionsActivity.this, "Error loading Recognition. Please check your network connection.", Toast.LENGTH_SHORT).show(); + } + }); + } catch (IllegalArgumentException e) { + progressDialog.dismiss(); + Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + } - @Override - public void onResponse(Call<List<Recognitions>> call, Response<List<Recognitions>> response) { - progressDialog.dismiss(); - generateDataList(response.body()); + } + private void generateCurrentActivity(Recognition recognition) { + TextView mCurrentActivityTitle = findViewById(R.id.text_recognition_current_title); + TextView mCurrentActivityTime = findViewById(R.id.text_recognition_current_time); + RecyclerView mCurrentActivityChangedItems = findViewById(R.id.rv_recognition_current_items); + mCurrentActivityTitle.setText(recognition.getDescription().toString()); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, Locale.US); + mCurrentActivityTime.setText(dateFormat.format(new Date(((long) recognition.getTimestamp()) * 1000))); + ArrayList<Widget> widgets = getIntent().getParcelableArrayListExtra("widgets"); + ArrayList<Widget> activityWidgets = new ArrayList<>(); + for (ChangedItem ci : recognition.getChangedItems()) { + for (Widget widget : widgets) { + if (widget.item() != null && ci.getName().equals(widget.item().name())) { + activityWidgets.add(widget); + } } + } - @Override - public void onFailure(Call<List<Recognitions>> call, Throwable t) { - progressDialog.dismiss(); - Toast.makeText(RecognitionsActivity.this, "Error loading Recognitions. Please check your network connection.", Toast.LENGTH_SHORT).show(); + try { + Connection c = ConnectionFactory.getUsableConnection(); + if (c != null) { + WidgetAdapter mChangedItemsAdapter = new WidgetAdapter(this, c, this); + mChangedItemsAdapter.update(activityWidgets, false); + mCurrentActivityChangedItems.setLayoutManager(new LinearLayoutManager(this)); + mCurrentActivityChangedItems.setAdapter(mChangedItemsAdapter); } - }); + } catch (ConnectionException e) { + Toast.makeText(this, "No connection", Toast.LENGTH_LONG).show(); + } + } + private void generatePastActivities(List<Recognition> recognitions) { + ArrayList<Recognition> filteredRecognitions = new ArrayList<>(); + for (Recognition r : recognitions) { + if (r.getType().equals("recognition")) { + filteredRecognitions.add(r); + } + } + mRvPastRecognitions = findViewById(R.id.rv_past_recognitions); + mPastRecognitionsAdapter = new RecognitionsAdapter(this, filteredRecognitions); + mRvPastRecognitions.setLayoutManager(new LinearLayoutManager(this)); + mRvPastRecognitions.setAdapter(mPastRecognitionsAdapter); } + /* // Generates a List using RecyclerView with the RecognitionsAdapter - private void generateDataList(List<Recognitions> recognitionsList) { + private void generateDataList(List<Recognition> recognitionsList) { recyclerView = findViewById(R.id.customRecyclerView); adapter = new RecognitionsAdapter(this,recognitionsList); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(RecognitionsActivity.this); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); - } + }*/ @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -116,4 +190,14 @@ public class RecognitionsActivity extends AppCompatActivity{ : R.string.recognitions_title; setTitle(titleResId); } + + @Override + public void onItemClicked(Widget item) { + Toast.makeText(this, "onItemClicked", Toast.LENGTH_SHORT).show(); + } + + @Override + public void onItemLongClicked(Widget item) { + Toast.makeText(this, "onItemLongClicked", Toast.LENGTH_SHORT).show(); + } } diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsAdapter.java b/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsAdapter.java index a07cae6cb55670bc7acdae87ef7f95798e093bb4..857312bb73b7f6e445f6bc1d196ed03423bd8424 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsAdapter.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/RecognitionsAdapter.java @@ -4,20 +4,16 @@ import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.Toast; -import org.json.JSONArray; import org.openhab.habdroid.R; import org.openhab.habdroid.model.ChangedItem; -import org.openhab.habdroid.model.Recognitions; +import org.openhab.habdroid.model.Recognition; import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; import androidx.recyclerview.widget.RecyclerView; @@ -29,40 +25,27 @@ import androidx.recyclerview.widget.RecyclerView; public class RecognitionsAdapter extends RecyclerView.Adapter<RecognitionsAdapter.CustomViewHolder> { - private List<Recognitions> dataList; - private Context context; -// private ArrayList<String> changedStates = new ArrayList<>(); + private List<Recognition> mRecognitions; + private Context mContext; - public RecognitionsAdapter(Context context, List<Recognitions> dataList/*, List<ChangedItem> changedItemList*/){ - this.context = context; - this.dataList = dataList; + public RecognitionsAdapter(Context context, List<Recognition> recognitions){ + this.mContext = context; + this.mRecognitions = recognitions; } class CustomViewHolder extends RecyclerView.ViewHolder { - public final View mView; - - TextView recDescription; - TextView recType; - TextView recActivity; - TextView recTimestamp; - TextView recChangedState; - -// ArrayList<String> cS = new ArrayList<>(); - + final View mView; + TextView recognitionTitle; + TextView recognitionTime; CustomViewHolder(View itemView) { super(itemView); mView = itemView; - - recDescription = mView.findViewById(R.id.recognitionDescription); - recType = mView.findViewById(R.id.recognitionType); - recActivity = mView.findViewById(R.id.recognitionActivity); - recTimestamp = mView.findViewById(R.id.recognitionTimestamp); - recChangedState = mView.findViewById(R.id.recognitionChangedState); - + recognitionTime = itemView.findViewById(R.id.text_recognition_past_time); + recognitionTitle = itemView.findViewById(R.id.text_recognition_past_title); } } @@ -70,52 +53,20 @@ public class RecognitionsAdapter extends RecyclerView.Adapter<RecognitionsAdapte @Override public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); - View view = layoutInflater.inflate(R.layout.custom_row, parent, false); + View view = layoutInflater.inflate(R.layout.list_item_past_recognition, parent, false); return new CustomViewHolder(view); } @Override public void onBindViewHolder(CustomViewHolder holder, int position) { - int identifier = dataList.get(position).getIdentifier(); - String type = dataList.get(position).getType(); - Object activity = dataList.get(position).getActivity(); - Object description = dataList.get(position).getDescription(); - int timestamp = dataList.get(position).getTimestamp(); - - // ChangedItem - String name; - String state; - String label; - - -// String changedItem = String.join(",", name, state, label); -// if (dataList.get(position).getChangedItems().size() == 3) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < dataList.get(position).getChangedItems().size(); i++) { - name = dataList.get(position).getChangedItems().get(i).getName(); - state = dataList.get(position).getChangedItems().get(i).getState(); - label = dataList.get(position).getChangedItems().get(i).getLabel(); - String changedItem = String.join(",", name, state, label); - -// changedStates.add(changedItem); - sb.append(changedItem).append('\n'); - -// Uncomment line 104 and comment out line 114 and 115 to see the alternate behavior -// holder.recChangedState1.setText(changedItem); - - } - - - holder.recDescription.setText((CharSequence) description); - holder.recType.setText(type); - holder.recActivity.setText((CharSequence) activity); - holder.recTimestamp.setText(DateFormat.getDateTimeInstance().format(timestamp)); - holder.recChangedState.setText(sb.toString()); - + Recognition item = mRecognitions.get(position); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT, Locale.US); + holder.recognitionTime.setText(dateFormat.format(new Date(((long)item.getTimestamp())*1000))); + holder.recognitionTitle.setText(item.getDescription().toString()); } @Override public int getItemCount() { - return dataList == null ? 0 : dataList.size(); + return mRecognitions == null ? 0 : mRecognitions.size(); } } diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java index 2618679b068096b5e3bebd06b7d0f36118ea0992..bbeef3f097efd03f32bf0d0e5d46133b830a3b5a 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.java @@ -19,6 +19,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; @@ -40,6 +41,7 @@ import android.widget.NumberPicker; import android.widget.RadioGroup; import android.widget.SeekBar; import android.widget.TextView; +import android.widget.Toast; import android.widget.VideoView; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; @@ -49,21 +51,30 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.snackbar.Snackbar; import com.larswerkman.holocolorpicker.ColorPicker; import com.larswerkman.holocolorpicker.SaturationBar; import com.larswerkman.holocolorpicker.ValueBar; import okhttp3.HttpUrl; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + import org.openhab.habdroid.R; import org.openhab.habdroid.core.connection.Connection; import org.openhab.habdroid.model.Item; import org.openhab.habdroid.model.LabeledValue; +import org.openhab.habdroid.model.Recognition; import org.openhab.habdroid.model.Widget; import org.openhab.habdroid.ui.widget.DividerItemDecoration; import org.openhab.habdroid.ui.widget.ExtendedSpinner; import org.openhab.habdroid.ui.widget.SegmentedControlButton; import org.openhab.habdroid.ui.widget.WidgetImageView; import org.openhab.habdroid.util.Constants; +import org.openhab.habdroid.util.GetDataService; import org.openhab.habdroid.util.MjpegStreamer; +import org.openhab.habdroid.util.RetrofitClientInstance; import org.openhab.habdroid.util.Util; import java.util.ArrayList; @@ -116,12 +127,10 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder public WidgetAdapter(Context context, Connection connection, ItemClickListener itemClickListener) { super(); - mInflater = LayoutInflater.from(context); mItemClickListener = itemClickListener; mConnection = connection; mColorMapper = new ColorMapper(context); - TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(R.attr.chartTheme, tv, true); mChartTheme = tv.string; @@ -995,11 +1004,47 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder } } + private final static Handler mSnackbarHandler = new Handler(); + private final static int DELAY_SNACKBAR = 1500; + + private static void showSnackbar(View v, int delay, Item item) { + mSnackbarHandler.removeCallbacksAndMessages(null); + final View currentView = v.getRootView(); + Snackbar snackbar = Snackbar.make(currentView, currentView.getContext().getString(R.string.snackbar_set_default, item.label()), Snackbar.LENGTH_LONG); + snackbar.setAction(currentView.getContext().getString(R.string.snackbar_btn_set_default), v1 -> { + try { + GetDataService service = RetrofitClientInstance.getRetrofitInstance(currentView.getContext()).create(GetDataService.class); + Call<ResponseBody> call = service.setNewDefaultValue(item.name()); + call.enqueue(new Callback<ResponseBody>() { + + @Override + public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { + Toast.makeText(currentView.getContext(), currentView.getContext().getString(R.string.toast_set_default_success), Toast.LENGTH_LONG).show(); + } + + @Override + public void onFailure(Call<ResponseBody> call, Throwable t) { + Toast.makeText(currentView.getContext(), "Failure", Toast.LENGTH_LONG).show(); + } + }); + } catch (IllegalArgumentException e) { + Toast.makeText(currentView.getContext(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + } + }); + mSnackbarHandler.postDelayed(() -> { + snackbar.show(); + }, delay); + + } + + + public static class ColorViewHolder extends LabeledItemBaseViewHolder implements View.OnTouchListener, Handler.Callback, ColorPicker.OnColorChangedListener { private Item mBoundItem; private final LayoutInflater mInflater; private final Handler mHandler = new Handler(this); + private boolean mColorChanged; ColorViewHolder(LayoutInflater inflater, ViewGroup parent, Connection conn, ColorMapper colorMapper) { @@ -1028,6 +1073,7 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder if (v.getTag() instanceof String) { final String cmd = (String) v.getTag(); Util.sendItemCommand(mConnection.getAsyncHttpClient(), mBoundItem, cmd); + WidgetAdapter.showSnackbar(itemView, DELAY_SNACKBAR, mBoundItem); } else { showColorPickerDialog(); } @@ -1063,18 +1109,29 @@ public class WidgetAdapter extends RecyclerView.Adapter<WidgetAdapter.ViewHolder colorPicker.setOnColorChangedListener(this); colorPicker.setShowOldCenterColor(false); + colorPicker.setOnColorSelectedListener(color -> mColorChanged = true); + saturationBar.setOnSaturationChangedListener(saturation -> mColorChanged = true); + valueBar.setOnValueChangedListener(value -> mColorChanged = true); + float[] initialColor = mBoundItem.stateAsHsv(); if (initialColor != null) { colorPicker.setColor(Color.HSVToColor(initialColor)); + } new AlertDialog.Builder(contentView.getContext()) .setView(contentView) - .setNegativeButton(R.string.close, null) - .show(); + .setOnDismissListener(dialog -> { + if (mColorChanged) { + WidgetAdapter.showSnackbar(itemView, 0, mBoundItem); + mColorChanged = false; + } + }) + .setNegativeButton(R.string.close, null).show(); } } + public static class MjpegVideoViewHolder extends ViewHolder { private final ImageView mImageView; private MjpegStreamer mStreamer; diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java index a1a949280f9324238bd36f480d8865e768045fac..84fa58260756bd309e377cc24329c212d32959b4 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java +++ b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetListFragment.java @@ -31,6 +31,7 @@ import org.openhab.habdroid.model.Widget; import org.openhab.habdroid.util.CacheManager; import org.openhab.habdroid.util.Util; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -80,7 +81,7 @@ public class WidgetListFragment extends Fragment Log.d(TAG, "onActivityCreated()"); Log.d(TAG, "isAdded = " + isAdded()); mActivity = (MainActivity) getActivity(); - + mActivity.updateWidgets(mWidgets); mAdapter = new WidgetAdapter(mActivity, mActivity.getConnection(), this); mLayoutManager = new LinearLayoutManager(mActivity); @@ -251,9 +252,10 @@ public class WidgetListFragment extends Fragment mAdapter.setSelectedPosition(-1); } + public ArrayList<Widget> mWidgets = new ArrayList<>(); public void update(String pageTitle, List<Widget> widgets) { mTitle = pageTitle; - + mWidgets.addAll(widgets); if (mAdapter != null) { mAdapter.update(widgets, mRefreshLayout.isRefreshing()); setHighlightedPageLink(mHighlightedPageLink); @@ -261,6 +263,7 @@ public class WidgetListFragment extends Fragment } if (mActivity != null && mIsVisible) { mActivity.updateTitle(); + mActivity.updateWidgets(widgets); } } diff --git a/mobile/src/main/java/org/openhab/habdroid/util/Constants.java b/mobile/src/main/java/org/openhab/habdroid/util/Constants.java index 6f6228526b5dad4ee0646890993ada7313e4f430..9ee7d48e74778d2206b17ea02df8eae7529e14f5 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/Constants.java +++ b/mobile/src/main/java/org/openhab/habdroid/util/Constants.java @@ -19,6 +19,7 @@ public class Constants { public static final String PREFERENCE_SITEMAP_LABEL = "default_openhab_sitemap_label"; public static final String PREFERENCE_REMOTE_URL = "default_openhab_alturl"; public static final String PREFERENCE_LOCAL_URL = "default_openhab_url"; + public static final String PREFERENCE_OPENLICHT_URL = "default_openlicht_url"; public static final String PREFERENCE_THEME = "default_openhab_theme"; public static final String PREFERENCE_ANIMATION = "default_openhab_animation"; public static final String PREFERENCE_DEMOMODE = "default_openhab_demomode"; diff --git a/mobile/src/main/java/org/openhab/habdroid/util/GetDataService.java b/mobile/src/main/java/org/openhab/habdroid/util/GetDataService.java index 9f8ae061c8a4769e44fa3da6b34308d1f1a400e8..a0dbe06b183dd572ac308a37588987931e28afd2 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/GetDataService.java +++ b/mobile/src/main/java/org/openhab/habdroid/util/GetDataService.java @@ -1,11 +1,14 @@ package org.openhab.habdroid.util; -import org.openhab.habdroid.model.Recognitions; +import org.openhab.habdroid.model.Recognition; import java.util.List; +import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.GET; +import retrofit2.http.PUT; +import retrofit2.http.Path; /* * OpenLicht @@ -16,8 +19,12 @@ import retrofit2.http.GET; public interface GetDataService { @GET("/events/long") - Call<List<Recognitions>> getAllRecognitions(); + Call<List<Recognition>> getAllRecognitions(); + + @PUT("/feedback/{identifier}/newDefault") + Call<ResponseBody> setNewDefaultValue(@Path("identifier") String identifier); + // @GET("/recognitions") -// Call<List<Recognitions>> getAllRecognitions(); +// Call<List<Recognition>> getAllRecognitions(); } diff --git a/mobile/src/main/java/org/openhab/habdroid/util/RetrofitClientInstance.java b/mobile/src/main/java/org/openhab/habdroid/util/RetrofitClientInstance.java index 24460184d9fef3ea56eeb648cca8e84743535dae..0b79119adab1e02e8a28afeb7d1d6458dd4f9f87 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/RetrofitClientInstance.java +++ b/mobile/src/main/java/org/openhab/habdroid/util/RetrofitClientInstance.java @@ -1,8 +1,12 @@ package org.openhab.habdroid.util; +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; - /* * OpenLicht * Use Retrofit to call the API @@ -12,12 +16,14 @@ import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitClientInstance { private static Retrofit retrofit; - private static final String BASE_URL = "http://10.0.2.2:5000"; - public static Retrofit getRetrofitInstance() { - if (retrofit == null) { + + public static Retrofit getRetrofitInstance(Context activity) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); + String url = preferences.getString(Constants.PREFERENCE_OPENLICHT_URL, ""); + if (retrofit == null || !retrofit.baseUrl().toString().equals(url)) { retrofit = new Retrofit.Builder() - .baseUrl(BASE_URL) + .baseUrl(url) .addConverterFactory(GsonConverterFactory.create()) .build(); } diff --git a/mobile/src/main/java/org/openhab/habdroid/util/Util.java b/mobile/src/main/java/org/openhab/habdroid/util/Util.java index 7bdd8393b392c5f19418e3d89b09e16c54c1caee..d6bcbfc6afc6537abed8459ab1d21e684e8d18d7 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/Util.java +++ b/mobile/src/main/java/org/openhab/habdroid/util/Util.java @@ -197,6 +197,8 @@ public class Util { sendItemCommand(client, item.link(), command); } + + public static void sendItemCommand(AsyncHttpClient client, String itemUrl, String command) { if (itemUrl == null || command == null) { return; diff --git a/mobile/src/main/res/drawable/ic_new_releases_black_24dp.xml b/mobile/src/main/res/drawable/ic_new_releases_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..ce9baf42d499b384e592f159b54c7aa730228c54 --- /dev/null +++ b/mobile/src/main/res/drawable/ic_new_releases_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M23,12l-2.44,-2.78 0.34,-3.68 -3.61,-0.82 -1.89,-3.18L12,3 8.6,1.54 6.71,4.72l-3.61,0.81 0.34,3.68L1,12l2.44,2.78 -0.34,3.69 3.61,0.82 1.89,3.18L12,21l3.4,1.46 1.89,-3.18 3.61,-0.82 -0.34,-3.68L23,12zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/> +</vector> diff --git a/mobile/src/main/res/layout/activity_recognitions.xml b/mobile/src/main/res/layout/activity_recognitions.xml index 3b3f81b2887b6504b200bcf203b3542b76d18553..5f29c8239e39f14cb53c870ca13cf6bffc5bf319 100644 --- a/mobile/src/main/res/layout/activity_recognitions.xml +++ b/mobile/src/main/res/layout/activity_recognitions.xml @@ -1,13 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> - -<!--Layout for OpenLicht Recognitions--> -<!--By Muhammad Ibrahim Rahman--> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" 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="vertical"> <androidx.appcompat.widget.Toolbar @@ -15,40 +10,78 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" - android:elevation="8dp" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" /> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + <androidx.swiperefreshlayout.widget.SwipeRefreshLayout + android:id="@+id/swipeRefresh_recognitions" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="?android:activatedBackgroundIndicator" - android:descendantFocusability="blocksDescendants"> - - <include layout="@layout/widgetlist_icontext" /> - - <ImageButton - style="@style/ActionButton" - android:id="@+id/up_button" - android:contentDescription="@string/content_description_color_up" - android:src="?themedArrowUpIcon" /> - - <ImageButton - style="@style/ActionButton" - android:id="@+id/select_color_button" - android:contentDescription="@string/content_description_open_color_wheel" - android:src="?themedColorIcon" /> - - <ImageButton - style="@style/ActionButton" - android:id="@+id/down_button" - android:contentDescription="@string/content_description_color_down" - android:src="?themedArrowDownIcon" /> - - </LinearLayout> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/customRecyclerView" - android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.google.android.material.card.MaterialCardView + android:id="@+id/card_recognition_current" + style="@style/Widget.MaterialComponents.CardView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/mtrl_card_spacing" + android:layout_marginTop="@dimen/mtrl_card_spacing" + android:layout_marginRight="@dimen/mtrl_card_spacing"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Current Activity" + android:textAllCaps="true" + android:textColor="@color/black" + android:textSize="10dp" /> + + <TextView + android:id="@+id/text_recognition_current_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/black" + android:textSize="24sp" /> + + <TextView + android:id="@+id/text_recognition_current_time" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/rv_recognition_current_items" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="16dp" /> + + </LinearLayout> + </com.google.android.material.card.MaterialCardView> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="24dp" + android:text="Past activities" + android:textAllCaps="true" + android:textColor="@color/black" + android:textSize="10dp" /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/rv_past_recognitions" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </LinearLayout> + </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </LinearLayout> \ No newline at end of file diff --git a/mobile/src/main/res/layout/custom_row.xml b/mobile/src/main/res/layout/custom_row.xml index 6f5d2a24af3ec375ac0a60ce562ca31ad49d0f9f..9639908cd5bfc6bb3032668347e390f73d0b647a 100644 --- a/mobile/src/main/res/layout/custom_row.xml +++ b/mobile/src/main/res/layout/custom_row.xml @@ -1,65 +1,62 @@ <?xml version="1.0" encoding="utf-8"?> -<!--OpenLicht--> -<!--Layout for RecyclerView--> -<!--By Muhammad Ibrahim Rahman--> +<!--OpenLicht--><!--Layout for RecyclerView--><!--By Muhammad Ibrahim Rahman--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" xmlns:card_view="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="6dp" - > + android:padding="6dp"> <androidx.cardview.widget.CardView - android:layout_width="match_parent" - android:layout_height="wrap_content" android:id="@+id/card_view" - card_view:cardUseCompatPadding="true" - card_view:cardCornerRadius="2dp" - - > - - <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="6dp" - > - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/recognitionDescription" - android:layout_alignParentTop="true" - android:textSize="30sp" - tools:text="Sample Text"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/recognitionType" - android:layout_below="@+id/recognitionDescription" - tools:text="Sample Text"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/recognitionActivity" - android:layout_below="@+id/recognitionType" - tools:text="Sample Text"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/recognitionTimestamp" - android:layout_below="@+id/recognitionActivity" - tools:text="Sample Text"/> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/recognitionChangedState" - android:layout_below="@+id/recognitionTimestamp" - tools:text="Sample Text"/> - - </RelativeLayout> + card_view:cardCornerRadius="2dp" + card_view:cardUseCompatPadding="true"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="6dp"> + + <TextView + android:id="@+id/recognitionDescription" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:textSize="30sp" + tools:text="Sample Text" /> + + <TextView + android:id="@+id/recognitionType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/recognitionDescription" + tools:text="Sample Text" /> + + <TextView + android:id="@+id/recognitionActivity" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/recognitionType" + tools:text="Sample Text" /> + + <TextView + android:id="@+id/recognitionTimestamp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/recognitionActivity" + tools:text="Sample Text" /> + + <TextView + android:id="@+id/recognitionChangedState" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/recognitionTimestamp" + tools:text="Sample Text" /> + + </RelativeLayout> </androidx.cardview.widget.CardView> </LinearLayout> diff --git a/mobile/src/main/res/layout/list_item_past_recognition.xml b/mobile/src/main/res/layout/list_item_past_recognition.xml new file mode 100644 index 0000000000000000000000000000000000000000..e22814d735ec8786245cf08054035242ad816337 --- /dev/null +++ b/mobile/src/main/res/layout/list_item_past_recognition.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.google.android.material.card.MaterialCardView + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Widget.MaterialComponents.CardView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/mtrl_card_spacing" + android:layout_marginTop="@dimen/mtrl_card_spacing" + android:layout_marginRight="@dimen/mtrl_card_spacing"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TextView + android:id="@+id/text_recognition_past_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/black" + android:textSize="18sp" /> + + <TextView + android:id="@+id/text_recognition_past_time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12sp"/> + + </LinearLayout> + </com.google.android.material.card.MaterialCardView> diff --git a/mobile/src/main/res/menu/left_drawer.xml b/mobile/src/main/res/menu/left_drawer.xml index fa525a2954e00bbfe2d425bc7dca5d2b288c739f..9ddf2bd0fb61727cf8f58c201d3b262a976d351d 100644 --- a/mobile/src/main/res/menu/left_drawer.xml +++ b/mobile/src/main/res/menu/left_drawer.xml @@ -8,8 +8,8 @@ <item android:id="@+id/recognitions" - android:icon="@drawable/ic_info_outline" - android:title="@string/recognitions" /> + android:icon="@drawable/ic_new_releases_black_24dp" + android:title="@string/mainmenu_openhab_recognitions" /> <item android:id="@+id/notifications" diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index 2ec1e4513ed87754f0859c184b8c8878e5691d53..d620c64fa2ccbf360c37b648af3b39057ac4d8bb 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -12,7 +12,7 @@ <!-- Main menu items --> <string name="mainmenu_openhab_sitemaps">Sitemaps</string> <!-- OpenLicht --> - <string name="recognitions">Recognitions</string> + <string name="mainmenu_openhab_recognitions">Recognitions</string> <!-- /OpenLicht--> <string name="mainmenu_openhab_preferences">Settings</string> <string name="mainmenu_openhab_selectsitemap">Select default Sitemap</string> @@ -58,6 +58,7 @@ <string name="settings_current_default_sitemap">Current default Sitemap: %1$s</string> <string name="settings_openhab_alt_connection">Remote</string> <string name="settings_openhab_connection">Local</string> + <string name="settings_openlicht_connection">OpenLicht REST URL</string> <string name="settings_notification_vibration">Notification vibration</string> <string name="settings_notification_vibration_off">Off</string> <string name="settings_notification_vibration_short">Short</string> @@ -69,6 +70,7 @@ <string name="settings_notification_vibration_value_twice" translatable="false">twice</string> <string name="settings_connection_summary">Connected to %s</string> <string name="settings_insecure_connection_summary">Insecurely connected to %s</string> + <string name="settings_openlicht_connection_summary">Set your Openlicht URL here</string> <!-- App messages strings --> <string name="title_voice_widget">openHAB voice commands</string> @@ -230,4 +232,8 @@ <string name="app_intro_skip_button">SKIP</string> <!-- Intro "DONE" button --> <string name="app_intro_done_button">DONE</string> + + <string name="snackbar_set_default">Set manual change as new default value for %1$s?</string> + <string name="toast_set_default_success">Manual change was set as new default value!</string> + <string name="snackbar_btn_set_default">Set as\ndefault</string> </resources> diff --git a/mobile/src/main/res/xml/preferences.xml b/mobile/src/main/res/xml/preferences.xml index 962af85bfcd2f9de955657fd2c48f976ab944f1e..6827efdc0cf884ac7373958fb9ff1746e84c0d58 100644 --- a/mobile/src/main/res/xml/preferences.xml +++ b/mobile/src/main/res/xml/preferences.xml @@ -20,6 +20,13 @@ android:title="@string/settings_openhab_alt_connection" android:dependency="default_openhab_demomode" android:icon="@drawable/ic_tree_grey_24dp" /> + <EditTextPreference + android:defaultValue="@string/empty_string" + android:inputType="textUri" + android:key="default_openlicht_url" + android:summary="@string/settings_openlicht_connection_summary" + android:title="@string/settings_openlicht_connection" + android:icon="@drawable/ic_earth_grey_24dp" /> <org.openhab.habdroid.ui.widget.SslClientCertificatePreference android:defaultValue="@string/settings_openhab_none" android:key="default_openhab_sslclientcert"