diff --git a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java index 009f9301f6a0473b1107fa1544a459acb500c41e..df1273ea45365632fdd3d1f46a4d1697afba98db 100644 --- a/ring-android/app/src/main/java/cx/ring/client/CallActivity.java +++ b/ring-android/app/src/main/java/cx/ring/client/CallActivity.java @@ -157,13 +157,13 @@ public class CallActivity extends AppCompatActivity { | View.SYSTEM_UI_FLAG_IMMERSIVE); CallFragment callFragment = getCallFragment(); - if(callFragment != null) { + if(callFragment != null && !callFragment.isPluginsMode()) { callFragment.toggleVideoPluginsCarousel(false); } } } - private void showSystemUI() { + public void showSystemUI() { if (mMainView != null) { mMainView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LOW_PROFILE @@ -192,6 +192,8 @@ public class CallActivity extends AppCompatActivity { public boolean onKeyDown(int keyCode, KeyEvent event) { CallFragment callFragment = getCallFragment(); if (callFragment != null) { + callFragment.toggleVideoPluginsCarousel(false); + callFragment.displayVideoPluginsCarousel(); return MediaButtonsHelper.handleMediaKeyCode(keyCode, callFragment) || super.onKeyDown(keyCode, event); } diff --git a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java index 146c9b1b3fb95fb82f3628c3021b67de18c9c210..e67455e951d572e400a3c0d7ccc3886eab20351a 100644 --- a/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java +++ b/ring-android/app/src/main/java/cx/ring/client/HomeActivity.java @@ -672,6 +672,7 @@ public class HomeActivity extends AppCompatActivity implements BottomNavigationV if (fContent instanceof PluginsListSettingsFragment) { return; } + fContent = new PluginsListSettingsFragment(); getSupportFragmentManager() .beginTransaction() diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java index b3348f4eae91b8fd848872e5dae56a8e9cd486e3..6a28c1b1743e608e4932815c40a34a66b237b6c7 100644 --- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java +++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java @@ -120,6 +120,7 @@ import cx.ring.utils.DeviceUtils; import cx.ring.utils.KeyboardVisibilityManager; import cx.ring.utils.Log; import cx.ring.utils.MediaButtonsHelper; +import cx.ring.utils.StringUtils; import cx.ring.views.AvatarDrawable; import io.reactivex.disposables.CompositeDisposable; @@ -169,6 +170,9 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements private boolean toggleVideoPluginsCarousel = true; private final ValueAnimator animation = new ValueAnimator(); + public boolean isPluginsMode() { + return pluginsMode; + } private PointF previewDrag = null; private final ValueAnimator previewSnapAnimation = new ValueAnimator(); @@ -336,7 +340,6 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements mPreviewSurfaceWidth = width; mPreviewSurfaceHeight = height; presenter.previewVideoSurfaceCreated(binding.previewSurface); -// presenter.pluginSurfaceCreated(binding.pluginPreviewSurface); } @Override @@ -349,7 +352,6 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { presenter.previewVideoSurfaceDestroyed(); -// presenter.pluginSurfaceDestroyed(); return true; } @@ -1385,20 +1387,19 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements if (pluginsModeFirst) { // Init callMediaHandlers = Ringservice.listCallMediaHandlers(); - List<Drawable> videoPluginsItems = new ArrayList<>(); + List<Drawable> videoPluginsItems = new ArrayList<>(callMediaHandlers.size() + 1); + videoPluginsItems.add(context.getDrawable(R.drawable.baseline_cancel_24)); // Search for plugin call media handlers icons // If a call media handler doesn't have an icon use a standard android icon for (String callMediaHandler : callMediaHandlers) { Map<String, String> details = getCallMediaHandlerDetails(callMediaHandler); - Drawable d = context.getDrawable(R.drawable.ic_jami); - if(details.containsKey("icoPath")){ - Drawable cd = PluginUtils.getIcon(details.get("icoPath")); - if(cd != null) { - d = cd; - } + String drawablePath = details.get("icoPath"); + Drawable handlerIcon = StringUtils.isEmpty(drawablePath) ? null : Drawable.createFromPath(details.get("icoPath")); + if (handlerIcon == null) { + handlerIcon = context.getDrawable(R.drawable.ic_jami); } - videoPluginsItems.add(d); + videoPluginsItems.add(handlerIcon); } rp.updateData(videoPluginsItems); @@ -1418,20 +1419,29 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements // Start loading the first or previous plugin if one was active if(callMediaHandlers.size() > 0) { // If no previous plugin was active, take the first, else previous - int position = (previousPluginPosition == -1)? 0 :previousPluginPosition; - String callMediaId = callMediaHandlers.get(position); - toggleCallMediaHandler(callMediaId, true); - previousPluginPosition = position; + int position; + if(previousPluginPosition < 1) { + rp.scrollToPosition(1); + position = 1; + previousPluginPosition = 1; + } else { + position = previousPluginPosition; + } + if (position > 0) { + String callMediaId = callMediaHandlers.get(position-1); + toggleCallMediaHandler(callMediaId, true); + } } } else { //change preview image displayVideoSurface(true,true); - if (previousPluginPosition != -1) { + if (previousPluginPosition > 0) { String callMediaId = callMediaHandlers. - get(previousPluginPosition); + get(previousPluginPosition-1); toggleCallMediaHandler(callMediaId, false); + rp.scrollToPosition(previousPluginPosition); } binding.recyclerPicker.setVisibility(View.GONE); movePreview(false); @@ -1446,18 +1456,44 @@ public class CallFragment extends BaseSupportFragment<CallPresenter> implements @Override public void onItemSelected(int position) { Log.i(TAG, "selected position: " + position); - if (previousPluginPosition != position) { + /** If there was a different plugin before, unload it + * If previousPluginPosition = -1 or 0, there was no plugin + */ + if (previousPluginPosition > 0) { + String callMediaId = callMediaHandlers.get(previousPluginPosition-1); + toggleCallMediaHandler(callMediaId, false); + } + + if (position > 0) { + previousPluginPosition = position; + String callMediaId = callMediaHandlers.get(position-1); + toggleCallMediaHandler(callMediaId, true); + } + } + + + /** + * Called whenever a plugin drawable in the recycler picker is clicked + * @param position + */ + @Override + public void onItemClicked(int position) { + Log.i(TAG, "selected position: " + position); + if (position == 0) { /** If there was a different plugin before, unload it - * If previousPluginPosition = -1, there was no plugin + * If previousPluginPosition = -1 or 0, there was no plugin */ - if (previousPluginPosition != -1) { - String callMediaId = callMediaHandlers.get(previousPluginPosition); + if (previousPluginPosition > 0) { + String callMediaId = callMediaHandlers.get(previousPluginPosition-1); toggleCallMediaHandler(callMediaId, false); + rp.scrollToPosition(previousPluginPosition); } - previousPluginPosition = position; - String callMediaId = callMediaHandlers.get(position); - toggleCallMediaHandler(callMediaId, true); + CallActivity callActivity = (CallActivity) getActivity(); + callActivity.showSystemUI(); + + toggleVideoPluginsCarousel(false); + displayVideoPluginsCarousel(); } } } \ No newline at end of file diff --git a/ring-android/app/src/main/java/cx/ring/plugins/PluginPreferences.java b/ring-android/app/src/main/java/cx/ring/plugins/PluginPreferences.java new file mode 100644 index 0000000000000000000000000000000000000000..33a04daa3303f05be33612f89b44f4d969b159fe --- /dev/null +++ b/ring-android/app/src/main/java/cx/ring/plugins/PluginPreferences.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Authors: Adrien Béraud <adrien.beraud@savoirfairelinux.com> + * Romain Bertozzi <romain.bertozzi@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +package cx.ring.plugins; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import cx.ring.R; +import cx.ring.databinding.FragPluginSettingsBinding; +import cx.ring.settings.pluginssettings.PluginDetails; + +public class PluginPreferences extends Preference { + private PluginDetails mPluginDetails; + + private View.OnClickListener resetClickListener; + private View.OnClickListener installClickListener; + + public void setResetClickListener(View.OnClickListener clickListener) { + resetClickListener = clickListener; + } + + public void setInstallClickListener(View.OnClickListener clickListener) { + installClickListener = clickListener; + } + + public PluginPreferences(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public PluginPreferences(Context context, PluginDetails pluginDetails) { + super(context); + mPluginDetails = pluginDetails; + setLayoutResource(R.layout.frag_plugin_settings); + } + + public PluginPreferences(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PluginPreferences(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + FragPluginSettingsBinding binding = FragPluginSettingsBinding.bind(holder.itemView); + + if (mPluginDetails != null) { + binding.pluginSettingIcon.setImageDrawable(mPluginDetails.getIcon()); + binding.pluginSettingTitle.setText(mPluginDetails.getName()); + } + + if (resetClickListener != null) { + binding.pluginSettingReset.setOnClickListener(resetClickListener); + } + + if (installClickListener != null) { + binding.pluginSettingInstall.setOnClickListener(installClickListener); + } + } + +} diff --git a/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java b/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java index f68ddf31e6be0458d3bdd15d3ad5a6749ceb03d5..3c5da8ba37c03a1fb736a1481cdf73049c01c574 100644 --- a/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java +++ b/ring-android/app/src/main/java/cx/ring/plugins/PluginUtils.java @@ -32,46 +32,25 @@ public class PluginUtils { tree(mContext.getFilesDir() + File.separator+ "plugins",0); tree(mContext.getCacheDir().getAbsolutePath(),0); - List<PluginDetails> pluginsList = new ArrayList<>(); - List<String> pluginsPaths = Ringservice.listAvailablePlugins(); - for(String pluginPath : pluginsPaths) { + List<PluginDetails> pluginsList = new ArrayList<>(pluginsPaths.size()); + for (String pluginPath : pluginsPaths) { File pluginFolder = new File(pluginPath); - if(pluginFolder.isDirectory()){ + if(pluginFolder.isDirectory()) { //We use the absolute path of a plugin as a preference name for uniqueness - SharedPreferences sp = mContext.getSharedPreferences( - pluginFolder.getName(), MODE_PRIVATE); - + //TODO remove and use enabled information from daemon + SharedPreferences sp = mContext.getSharedPreferences(pluginFolder.getName(), MODE_PRIVATE); boolean enabled = sp.getBoolean(PLUGIN_ENABLED,false); pluginsList.add(new PluginDetails( pluginFolder.getName(), - pluginFolder.getAbsolutePath(),enabled)); + pluginFolder.getAbsolutePath(), enabled)); } } return pluginsList; } - public static String getABI(){ - return Build.SUPPORTED_ABIS[0]; - } - - /** - * Checks if there is a file at the indicated path and converts if to a drawable if possible - * @param iconPath String representing the absolute icon path - * @return Drawable of the icon - */ - public static Drawable getIcon(String iconPath) { - Drawable icon = null; - File file = new File(iconPath); - Log.i(TAG, "Icon path: " + iconPath); - if(file.exists()) { - icon = Drawable.createFromPath(iconPath); - } - return icon; - } - /** * Loads the so file and instantiates the plugin init function (toggle on) * @param path root path of the plugin diff --git a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPicker.java b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPicker.java index f648d3c590c9990dffae918f6a18fa4ebaef5f36..fd1a83b1e72e544242b01c6f92548aa3af3a7dc7 100644 --- a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPicker.java +++ b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPicker.java @@ -57,8 +57,9 @@ public class RecyclerPicker implements RecyclerPickerAdapter.ItemClickListener{ int currentPos = mLayoutManager.findFirstVisibleItemPosition(); if(position != currentPos) { mRecyclerView.smoothScrollToPosition(position); - } else { mItemSelectedListener.onItemSelected(position); + } else { + mItemSelectedListener.onItemClicked(position); } } diff --git a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java index 9fa779b81b31336c91ed65c42b873149846132bb..c93c1ffa8046de8dd041697ba1f067b9cfc4b838 100644 --- a/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java +++ b/ring-android/app/src/main/java/cx/ring/plugins/RecyclerPicker/RecyclerPickerLayoutManager.java @@ -99,5 +99,6 @@ public class RecyclerPickerLayoutManager extends LinearLayoutManager { public interface ItemSelectedListener { void onItemSelected(int position); + void onItemClicked(int position); } } diff --git a/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java index 44696b1574df5ebf1a14519a3abf3f64bcb0dbfb..edb0ce785e46ec06eeb5c962315f240816759c55 100644 --- a/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java +++ b/ring-android/app/src/main/java/cx/ring/settings/SettingsFragment.java @@ -134,8 +134,9 @@ public class SettingsFragment extends BaseSupportFragment<SettingsPresenter> imp .show()); binding.settingsPluginsLayout.setOnClickListener(v -> { HomeActivity activity = (HomeActivity) getActivity(); - if (activity != null && presenter.getPluginsEnabled()) + if (activity != null && presenter.getPluginsEnabled()){ activity.goToPluginsListSettings(); + } }); } diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java index 408e505e649c9767ccffd763ade59ec1cf700986..f0a9147b68f18db25c6118d0ad66d10ad9072345 100644 --- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java +++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginSettingsFragment.java @@ -1 +1,387 @@ -package cx.ring.settings.pluginssettings; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.preference.CheckBoxPreference; import androidx.preference.DialogPreference; import androidx.preference.DropDownPreference; import androidx.preference.EditTextPreference; import androidx.preference.ListPreference; import androidx.preference.MultiSelectListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.preference.SeekBarPreference; import androidx.preference.SwitchPreference; import androidx.preference.TwoStatePreference; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import cx.ring.R; import cx.ring.client.HomeActivity; import cx.ring.daemon.Ringservice; import cx.ring.plugins.ButtonPreference.ButtonPreference; import static cx.ring.plugins.PluginUtils.getOrElse; import static cx.ring.plugins.PluginUtils.stringListToListString; public class PluginSettingsFragment extends PreferenceFragmentCompat { public static final String TAG = PluginSettingsFragment.class.getSimpleName(); private Context mContext; private List<Map<String, String>> mPreferencesAttributes; private PluginDetails pluginDetails; public static PluginSettingsFragment newInstance(PluginDetails pluginDetails) { Bundle args = new Bundle(); args.putString("name", pluginDetails.getName()); args.putString("rootPath", pluginDetails.getRootPath()); args.putBoolean("enabled", pluginDetails.isEnabled()); PluginSettingsFragment psf = new PluginSettingsFragment(); psf.setArguments(args); return psf; } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext = requireActivity(); Bundle arguments = getArguments(); if (arguments != null && arguments.getString("name") != null && arguments.getString("rootPath") != null) { pluginDetails = new PluginDetails(arguments.getString("name"), arguments.getString("rootPath"), arguments.getBoolean("enabled")); mPreferencesAttributes = pluginDetails.getPluginPreferences(); PreferenceManager preferenceManager = getPreferenceManager(); PluginPreferencesDataStore ppds = new PluginPreferencesDataStore(pluginDetails); preferenceManager.setPreferenceDataStore(ppds); setHasOptionsMenu(true); } } @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root = super.onCreateView(inflater, container, savedInstanceState); PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(mContext); screen.addPreference(createResetPreference()); for (Preference preference : createPreferences(mPreferencesAttributes)) { screen.addPreference(preference); } setPreferenceScreen(screen); return root; } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { inflater.inflate(R.menu.plugin_edition, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { switch (item.getItemId()) { case R.id.menuitem_delete: AlertDialog alertDialog = new MaterialAlertDialogBuilder(mContext) .setMessage(R.string.account_delete_dialog_message) .setTitle(R.string.plugin_uninstall_title) .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { Ringservice.uninstallPlugin(pluginDetails.getRootPath()); ((HomeActivity) requireActivity()).popFragmentImmediate(); }) .setNegativeButton(android.R.string.cancel, null) .create(); alertDialog.show(); break; default: break; } return super.onOptionsItemSelected(item); } /** * Takes a list of preferences attributes map * Creates a preference View for each map of attributes in the preference * * @param preferencesAttributes A list of preferences attributes * @return list of preferences */ private List<Preference> createPreferences(List<Map<String, String>> preferencesAttributes) { List<Preference> preferencesViews = new ArrayList<>(); if (preferencesAttributes != null) { for (Map<String, String> preferenceAttributes : preferencesAttributes) { String type = preferenceAttributes.get("type"); // Call for each type the appropriate function member if (type != null) { switch (type) { case "CheckBox": preferencesViews.add(createCheckBoxPreference(preferenceAttributes)); break; case "DropDown": preferencesViews.add(createDropDownPreference(preferenceAttributes)); break; case "EditText": preferencesViews.add(createEditTextPreference(preferenceAttributes)); break; case "List": preferencesViews.add(createListPreference(preferenceAttributes)); break; case "MultiSelectList": preferencesViews. add(createMultiSelectListPreference(preferenceAttributes)); break; case "SeekBar": preferencesViews.add(createSeekBarPreference(preferenceAttributes)); break; case "Switch": preferencesViews.add(createSwitchPreference(preferenceAttributes)); break; default: break; } } } } return preferencesViews; } private Preference createResetPreference() { ButtonPreference preference = new ButtonPreference(mContext); preference.setTitle(R.string.plugin_reset_preferences); preference.setSummary(R.string.plugin_reset_preferences_summary); preference.setIcon(R.drawable.baseline_cancel_24); preference.setPersistent(false); preference.setKey("uninstall"); preference.setText("Reset"); preference.setClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new MaterialAlertDialogBuilder(mContext) .setTitle(preference.getTitle()) .setMessage(R.string.plugin_reset_preferences_ask) .setPositiveButton(android.R.string.ok, (dialog, id) -> { Ringservice.resetPluginPreferencesValues(pluginDetails.getRootPath()); ((HomeActivity) requireActivity()).popFragmentImmediate(); }) .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> { /* Terminate with no action */ }) .show(); } }); return preference; } private CheckBoxPreference createCheckBoxPreference(Map<String, String> preferenceModel) { CheckBoxPreference preference = new CheckBoxPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setTwoStatePreferenceAttributes(preference, preferenceModel); return preference; } private DropDownPreference createDropDownPreference(Map<String, String> preferenceModel) { DropDownPreference preference = new DropDownPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setListPreferenceAttributes(preference, preferenceModel); return preference; } private EditTextPreference createEditTextPreference(Map<String, String> preferenceModel) { EditTextPreference preference = new EditTextPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setDialogPreferenceAttributes(preference, preferenceModel); setEditTextAttributes(preference, preferenceModel); return preference; } private ListPreference createListPreference(Map<String, String> preferenceModel) { ListPreference preference = new ListPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setListPreferenceAttributes(preference, preferenceModel); return preference; } private MultiSelectListPreference createMultiSelectListPreference( Map<String, String> preferenceModel) { MultiSelectListPreference preference = new MultiSelectListPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setMultiSelectListPreferenceAttributes(preference, preferenceModel); return preference; } private SwitchPreference createSwitchPreference(Map<String, String> preferenceModel) { SwitchPreference preference = new SwitchPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setTwoStatePreferenceAttributes(preference, preferenceModel); return preference; } private SeekBarPreference createSeekBarPreference(Map<String, String> preferenceModel) { SeekBarPreference preference = new SeekBarPreference(mContext); setPreferenceAttributes(preference, preferenceModel); setSeekBarPreferenceAttributes(preference, preferenceModel); return preference; } /** * Sets attributes that are common in the Preference base class * * @param preference the preference * @param preferenceModel the map of attributes */ private void setPreferenceAttributes(Preference preference, Map<String, String> preferenceModel) { String key = preferenceModel.get("key"); preference.setKey(key); preference.setTitle(getOrElse(preferenceModel.get("title"), "")); preference.setSummary(getOrElse(preferenceModel.get("summary"), "")); } //TODO : add drawable icon private void setDialogPreferenceAttributes(DialogPreference preference, Map<String, String> preferenceModel) { String dialogTitle = getOrElse(preferenceModel.get("dialogTitle"), ""); String dialogMessage = getOrElse(preferenceModel.get("dialogMessage"), ""); String positiveButtonText = getOrElse(preferenceModel.get("positiveButtonText"), ""); String negativeButtonText = getOrElse(preferenceModel.get("negativeButtonText"), ""); if (!dialogTitle.isEmpty()) { preference.setDialogTitle(dialogTitle); } if (!dialogMessage.isEmpty()) { preference.setDialogTitle(dialogMessage); } if (!positiveButtonText.isEmpty()) { preference.setPositiveButtonText(positiveButtonText); } if (!negativeButtonText.isEmpty()) { preference.setNegativeButtonText(negativeButtonText); } } /** * Sets attributes specific to EditTextPreference * Here we set the default value * * @param preference EditTextPreference * @param preferenceModel the map of attributes */ private void setEditTextAttributes(EditTextPreference preference, Map<String, String> preferenceModel) { preference.setDefaultValue(preferenceModel.get("defaultValue")); } /** * Sets specific attributes for Preference that have for a base class ListPreference * Sets the entries, entryValues and defaultValue * * @param preference the list preference * @param preferenceModel the map of attributes */ private void setListPreferenceAttributes(ListPreference preference, Map<String, String> preferenceModel) { setDialogPreferenceAttributes(preference, preferenceModel); String entries = getOrElse(preferenceModel.get("entries"), "[]"); preference.setEntries(stringListToListString(entries). toArray(new CharSequence[ 0 ])); String entryValues = getOrElse(preferenceModel.get("entryValues"), "[]"); preference.setEntryValues(stringListToListString(entryValues). toArray(new CharSequence[ 0 ])); preference.setDefaultValue(preferenceModel.get("defaultValue")); } /** * Sets specific attributes for Preference that have for a base class MultiSelectListPreference * Sets the entries, entryValues and defaultValues * * @param preference the multi select list preference * @param preferenceModel the map of attributes */ private void setMultiSelectListPreferenceAttributes(MultiSelectListPreference preference, Map<String, String> preferenceModel) { setDialogPreferenceAttributes(preference, preferenceModel); String entries = getOrElse(preferenceModel.get("entries"), "[]"); preference.setEntries(stringListToListString(entries). toArray(new CharSequence[ 0 ])); String entryValues = getOrElse(preferenceModel.get("entryValues"), "[]"); preference.setEntryValues(stringListToListString(entryValues). toArray(new CharSequence[ 0 ])); String defaultValues = getOrElse(preferenceModel.get("defaultValue"), "[]"); preference.setEntryValues(stringListToListString(entryValues). toArray(new CharSequence[ 0 ])); Set<CharSequence> set = new HashSet<>(stringListToListString(defaultValues)); preference.setDefaultValue(set); } /** * Sets specific attributes for setSeekBarPreference * * @param preference the seek bar preference * @param preferenceModel the map of attributes */ private void setSeekBarPreferenceAttributes(SeekBarPreference preference, Map<String, String> preferenceModel) { int min = 0, max = 1, increment = 1; int defaultValue = 0; try { min = Integer.parseInt(getOrElse(preferenceModel.get("min"), "0")); max = Integer.parseInt(getOrElse(preferenceModel.get("max"), "1")); increment = Integer.parseInt(getOrElse(preferenceModel.get("increment"), "1")); defaultValue = Integer.parseInt(getOrElse(preferenceModel.get("defaultValue"), "[0")); } catch (NumberFormatException e) { Log.e(TAG, e.toString()); } preference.setMin(min); preference.setMax(max); preference.setSeekBarIncrement(increment); preference.setAdjustable(Boolean.valueOf(getOrElse(preferenceModel.get("adjustable"), "true"))); preference.setDefaultValue(defaultValue); preference.setShowSeekBarValue(true); preference.setUpdatesContinuously(true); } /** * Sets specific attributes for twoStatePreference like Switch and CheckBox * * @param preference the two state preference * @param preferenceModel the map of attributes */ private void setTwoStatePreferenceAttributes(TwoStatePreference preference, Map<String, String> preferenceModel) { preference.setDefaultValue(Boolean.valueOf(preferenceModel.get("defaultValue"))); } } \ No newline at end of file +package cx.ring.settings.pluginssettings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.CheckBoxPreference; +import androidx.preference.DialogPreference; +import androidx.preference.DropDownPreference; +import androidx.preference.EditTextPreference; +import androidx.preference.ListPreference; +import androidx.preference.MultiSelectListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.preference.SeekBarPreference; +import androidx.preference.SwitchPreference; +import androidx.preference.TwoStatePreference; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.prefs.Preferences; + +import cx.ring.R; +import cx.ring.client.HomeActivity; +import cx.ring.daemon.Ringservice; +import cx.ring.plugins.ButtonPreference.ButtonPreference; +import cx.ring.plugins.PluginPreferences; + +import static android.content.Context.MODE_PRIVATE; +import static cx.ring.plugins.PluginUtils.PLUGIN_ENABLED; +import static cx.ring.plugins.PluginUtils.getOrElse; +import static cx.ring.plugins.PluginUtils.stringListToListString; + +public class PluginSettingsFragment extends PreferenceFragmentCompat { + public static final String TAG = PluginSettingsFragment.class.getSimpleName(); + private Context mContext; + private List<Map<String, String>> mPreferencesAttributes; + private PluginDetails pluginDetails; + + public static PluginSettingsFragment newInstance(PluginDetails pluginDetails) { + Bundle args = new Bundle(); + args.putString("name", pluginDetails.getName()); + args.putString("rootPath", pluginDetails.getRootPath()); + args.putBoolean("enabled", pluginDetails.isEnabled()); + PluginSettingsFragment psf = new PluginSettingsFragment(); + psf.setArguments(args); + return psf; + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mContext = requireActivity(); + + Bundle arguments = getArguments(); + + if (arguments != null && arguments.getString("name") != null + && arguments.getString("rootPath") != null) { + pluginDetails = new PluginDetails(arguments.getString("name"), + arguments.getString("rootPath"), arguments.getBoolean("enabled")); + + mPreferencesAttributes = pluginDetails.getPluginPreferences(); + + PreferenceManager preferenceManager = getPreferenceManager(); + PluginPreferencesDataStore ppds = new PluginPreferencesDataStore(pluginDetails); + preferenceManager.setPreferenceDataStore(ppds); + setHasOptionsMenu(true); + } + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View root = super.onCreateView(inflater, container, savedInstanceState); + + PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(mContext); + screen.addPreference(createHeadPreference()); + for (Preference preference : createPreferences(mPreferencesAttributes)) { + screen.addPreference(preference); + } + setPreferenceScreen(screen); + return root; + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + inflater.inflate(R.menu.plugin_edition, menu); + MenuItem item = menu.findItem(R.id.menuitem_delete); + item.setVisible(false); + super.onCreateOptionsMenu(menu, inflater); + } + + /** + * Takes a list of preferences attributes map + * Creates a preference View for each map of attributes in the preference + * + * @param preferencesAttributes A list of preferences attributes + * @return list of preferences + */ + private List<Preference> createPreferences(List<Map<String, String>> preferencesAttributes) { + List<Preference> preferencesViews = new ArrayList<>(); + if (preferencesAttributes != null) { + for (Map<String, String> preferenceAttributes : preferencesAttributes) { + String type = preferenceAttributes.get("type"); + // Call for each type the appropriate function member + if (type != null) { + switch (type) { + case "CheckBox": + preferencesViews.add(createCheckBoxPreference(preferenceAttributes)); + break; + case "DropDown": + preferencesViews.add(createDropDownPreference(preferenceAttributes)); + break; + case "EditText": + preferencesViews.add(createEditTextPreference(preferenceAttributes)); + break; + case "List": + preferencesViews.add(createListPreference(preferenceAttributes)); + break; + case "MultiSelectList": + preferencesViews. + add(createMultiSelectListPreference(preferenceAttributes)); + break; + case "SeekBar": + preferencesViews.add(createSeekBarPreference(preferenceAttributes)); + break; + case "Switch": + preferencesViews.add(createSwitchPreference(preferenceAttributes)); + break; + default: + break; + } + } + } + } + + return preferencesViews; + } + + private Preference createHeadPreference() + { + PluginPreferences preference = new PluginPreferences(mContext, pluginDetails); + preference.setResetClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new MaterialAlertDialogBuilder(mContext) + .setTitle(preference.getTitle()) + .setMessage(R.string.plugin_reset_preferences_ask) + .setPositiveButton(android.R.string.ok, (dialog, id) -> { + Ringservice.resetPluginPreferencesValues(pluginDetails.getRootPath()); + ((HomeActivity) requireActivity()).popFragmentImmediate(); + }) + .setNegativeButton(android.R.string.cancel, (dialog, whichButton) -> { + /* Terminate with no action */ + }) + .show(); + } + }); + preference.setInstallClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new MaterialAlertDialogBuilder(mContext) + .setMessage(R.string.account_delete_dialog_message) + .setTitle(R.string.plugin_uninstall_title) + .setPositiveButton(android.R.string.ok, (dialog, whichButton) -> { + SharedPreferences sp = mContext.getSharedPreferences( + pluginDetails.getName(), MODE_PRIVATE); + + SharedPreferences.Editor preferencesEditor = sp.edit(); + pluginDetails.setEnabled(false); + preferencesEditor.putBoolean(PLUGIN_ENABLED, pluginDetails.isEnabled()); + preferencesEditor.apply(); + Ringservice.uninstallPlugin(pluginDetails.getRootPath()); + ((HomeActivity) requireActivity()).popFragmentImmediate(); + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + }); + return preference; + } + + private CheckBoxPreference createCheckBoxPreference(Map<String, String> preferenceModel) { + CheckBoxPreference preference = new CheckBoxPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setTwoStatePreferenceAttributes(preference, preferenceModel); + return preference; + } + + private DropDownPreference createDropDownPreference(Map<String, String> preferenceModel) { + DropDownPreference preference = new DropDownPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setListPreferenceAttributes(preference, preferenceModel); + return preference; + } + + private EditTextPreference createEditTextPreference(Map<String, String> preferenceModel) { + EditTextPreference preference = new EditTextPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setDialogPreferenceAttributes(preference, preferenceModel); + setEditTextAttributes(preference, preferenceModel); + return preference; + } + + private ListPreference createListPreference(Map<String, String> preferenceModel) { + ListPreference preference = new ListPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setListPreferenceAttributes(preference, preferenceModel); + return preference; + } + + private MultiSelectListPreference createMultiSelectListPreference( + Map<String, String> preferenceModel) { + MultiSelectListPreference preference = new MultiSelectListPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setMultiSelectListPreferenceAttributes(preference, preferenceModel); + return preference; + } + + private SwitchPreference createSwitchPreference(Map<String, String> preferenceModel) { + SwitchPreference preference = new SwitchPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setTwoStatePreferenceAttributes(preference, preferenceModel); + return preference; + } + + private SeekBarPreference createSeekBarPreference(Map<String, String> preferenceModel) { + SeekBarPreference preference = new SeekBarPreference(mContext); + setPreferenceAttributes(preference, preferenceModel); + setSeekBarPreferenceAttributes(preference, preferenceModel); + return preference; + } + + /** + * Sets attributes that are common in the Preference base class + * + * @param preference the preference + * @param preferenceModel the map of attributes + */ + private void setPreferenceAttributes(Preference preference, + Map<String, String> preferenceModel) { + String key = preferenceModel.get("key"); + preference.setKey(key); + preference.setTitle(getOrElse(preferenceModel.get("title"), "")); + preference.setSummary(getOrElse(preferenceModel.get("summary"), "")); + } + + //TODO : add drawable icon + private void setDialogPreferenceAttributes(DialogPreference preference, + Map<String, String> preferenceModel) { + String dialogTitle = getOrElse(preferenceModel.get("dialogTitle"), ""); + String dialogMessage = getOrElse(preferenceModel.get("dialogMessage"), ""); + String positiveButtonText = getOrElse(preferenceModel.get("positiveButtonText"), ""); + String negativeButtonText = getOrElse(preferenceModel.get("negativeButtonText"), ""); + + if (!dialogTitle.isEmpty()) { + preference.setDialogTitle(dialogTitle); + } + + if (!dialogMessage.isEmpty()) { + preference.setDialogTitle(dialogMessage); + } + + if (!positiveButtonText.isEmpty()) { + preference.setPositiveButtonText(positiveButtonText); + } + + if (!negativeButtonText.isEmpty()) { + preference.setNegativeButtonText(negativeButtonText); + } + } + + /** + * Sets attributes specific to EditTextPreference + * Here we set the default value + * + * @param preference EditTextPreference + * @param preferenceModel the map of attributes + */ + private void setEditTextAttributes(EditTextPreference preference, + Map<String, String> preferenceModel) { + preference.setDefaultValue(preferenceModel.get("defaultValue")); + } + + /** + * Sets specific attributes for Preference that have for a base class ListPreference + * Sets the entries, entryValues and defaultValue + * + * @param preference the list preference + * @param preferenceModel the map of attributes + */ + private void setListPreferenceAttributes(ListPreference preference, + Map<String, String> preferenceModel) { + setDialogPreferenceAttributes(preference, preferenceModel); + String entries = getOrElse(preferenceModel.get("entries"), "[]"); + preference.setEntries(stringListToListString(entries). + toArray(new CharSequence[ 0 ])); + String entryValues = getOrElse(preferenceModel.get("entryValues"), "[]"); + preference.setEntryValues(stringListToListString(entryValues). + toArray(new CharSequence[ 0 ])); + preference.setDefaultValue(preferenceModel.get("defaultValue")); + } + + /** + * Sets specific attributes for Preference that have for a base class MultiSelectListPreference + * Sets the entries, entryValues and defaultValues + * + * @param preference the multi select list preference + * @param preferenceModel the map of attributes + */ + private void setMultiSelectListPreferenceAttributes(MultiSelectListPreference preference, + Map<String, String> preferenceModel) { + setDialogPreferenceAttributes(preference, preferenceModel); + String entries = getOrElse(preferenceModel.get("entries"), "[]"); + preference.setEntries(stringListToListString(entries). + toArray(new CharSequence[ 0 ])); + String entryValues = getOrElse(preferenceModel.get("entryValues"), "[]"); + preference.setEntryValues(stringListToListString(entryValues). + toArray(new CharSequence[ 0 ])); + String defaultValues = getOrElse(preferenceModel.get("defaultValue"), "[]"); + preference.setEntryValues(stringListToListString(entryValues). + toArray(new CharSequence[ 0 ])); + Set<CharSequence> set = new HashSet<>(stringListToListString(defaultValues)); + preference.setDefaultValue(set); + } + + /** + * Sets specific attributes for setSeekBarPreference + * + * @param preference the seek bar preference + * @param preferenceModel the map of attributes + */ + private void setSeekBarPreferenceAttributes(SeekBarPreference preference, + Map<String, String> preferenceModel) { + int min = 0, max = 1, increment = 1; + int defaultValue = 0; + try { + min = Integer.parseInt(getOrElse(preferenceModel.get("min"), "0")); + max = Integer.parseInt(getOrElse(preferenceModel.get("max"), "1")); + increment = Integer.parseInt(getOrElse(preferenceModel.get("increment"), "1")); + defaultValue = Integer.parseInt(getOrElse(preferenceModel.get("defaultValue"), "[0")); + } catch (NumberFormatException e) { + Log.e(TAG, e.toString()); + } + preference.setMin(min); + preference.setMax(max); + preference.setSeekBarIncrement(increment); + preference.setAdjustable(Boolean.valueOf(getOrElse(preferenceModel.get("adjustable"), + "true"))); + preference.setDefaultValue(defaultValue); + preference.setShowSeekBarValue(true); + preference.setUpdatesContinuously(true); + } + + /** + * Sets specific attributes for twoStatePreference like Switch and CheckBox + * + * @param preference the two state preference + * @param preferenceModel the map of attributes + */ + private void setTwoStatePreferenceAttributes(TwoStatePreference preference, + Map<String, String> preferenceModel) { + preference.setDefaultValue(Boolean.valueOf(preferenceModel.get("defaultValue"))); + } +} diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java index 61c3f51a7c8596581e49bd181f9fdab91a847ee1..8e0a1734356b7451bf35cf2d3aaaa1bfeb3664ec 100644 --- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java +++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListAdapter.java @@ -49,6 +49,7 @@ public class PluginsListAdapter extends RecyclerView.Adapter<PluginsListAdapter. public void updatePluginsList(List<PluginDetails> listPlugins) { mList = listPlugins; + notifyDataSetChanged(); } diff --git a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java index c9319d9a159f7ddbdcafdaa4a35e477b72084b06..4b6b73a44298e35f0fe944e2fe116fff80966ed9 100644 --- a/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java +++ b/ring-android/app/src/main/java/cx/ring/settings/pluginssettings/PluginsListSettingsFragment.java @@ -24,6 +24,7 @@ import com.google.android.material.snackbar.Snackbar; import java.io.File; import java.io.IOException; +import java.util.List; import cx.ring.R; import cx.ring.client.HomeActivity; @@ -85,13 +86,16 @@ public class PluginsListSettingsFragment extends Fragment implements PluginsList startActivityForResult(intent, ARCHIVE_REQUEST_CODE); }); + ((HomeActivity) requireActivity()). + setToolbarState(R.string.menu_item_plugin_list); + return pluginsSettingsList; } @Override public void onResume() { ((HomeActivity) requireActivity()). - setToolbarState(R.string.menu_item_settings); + setToolbarState(R.string.menu_item_plugin_list); super.onResume(); } @@ -146,6 +150,7 @@ public class PluginsListSettingsFragment extends Fragment implements PluginsList private String installPluginFile(File pluginFile, boolean force) throws IOException{ int i = Ringservice.installPlugin(pluginFile.getAbsolutePath(), force); + // Free the cache boolean cacheFileFreed = pluginFile.delete(); if(!cacheFileFreed) { @@ -181,6 +186,16 @@ public class PluginsListSettingsFragment extends Fragment implements PluginsList @Override public void onSuccess(String filename) { + + String[] plugin = filename.split(".jpl"); + List<PluginDetails> availablePlugins = listAvailablePlugins(mContext); + for (PluginDetails availablePlugin : availablePlugins){ + if (availablePlugin.getName().equals(plugin[0])) + { + availablePlugin.setEnabled(true); + onPluginEnabled(availablePlugin); + } + } ((PluginsListAdapter) mAdapter) .updatePluginsList(listAvailablePlugins(mContext)); Toast.makeText(mContext, "Plugin: " + filename + diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java index 972d48b95d5b7e8b969d55dc5e090c0084cb83d3..b8d700218de024e4e44dfa4d0db0b3bd10d850e8 100644 --- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java +++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java @@ -504,12 +504,6 @@ public class TVCallFragment extends BaseSupportFragment<CallPresenter> implement @Override public void resetPluginPreviewVideoSize(int previewWidth, int previewHeight, int rot) { - if (previewWidth == -1 && previewHeight == -1) - return; - mPreviewWidth = previewWidth; - mPreviewHeight = previewHeight; - boolean flip = (rot % 180) != 0; - binding.pluginPreviewSurface.setAspectRatio(flip ? mPreviewHeight : mPreviewWidth, flip ? mPreviewWidth : mPreviewHeight); } @Override diff --git a/ring-android/app/src/main/res/drawable/round_settings_backup_restore_24.xml b/ring-android/app/src/main/res/drawable/round_settings_backup_restore_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..2bf908c7f8b40d53a46c45b09bf54371f6e27a32 --- /dev/null +++ b/ring-android/app/src/main/res/drawable/round_settings_backup_restore_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12.26,3C7.17,2.86 3,6.95 3,12L1.21,12c-0.45,0 -0.67,0.54 -0.35,0.85l2.79,2.79c0.2,0.2 0.51,0.2 0.71,0l2.79,-2.79c0.31,-0.31 0.09,-0.85 -0.36,-0.85L5,12c0,-3.9 3.18,-7.05 7.1,-7 3.72,0.05 6.85,3.18 6.9,6.9 0.05,3.91 -3.1,7.1 -7,7.1 -1.25,0 -2.42,-0.34 -3.44,-0.91 -0.39,-0.22 -0.87,-0.14 -1.18,0.18 -0.46,0.46 -0.37,1.25 0.2,1.57C8.89,20.57 10.39,21 12,21c5.05,0 9.14,-4.17 9,-9.26 -0.13,-4.69 -4.05,-8.61 -8.74,-8.74z"/> +</vector> diff --git a/ring-android/app/src/main/res/layout/frag_plugin_settings.xml b/ring-android/app/src/main/res/layout/frag_plugin_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..a351b7a4ca32b4e10e04675a9935c35da1d77ed4 --- /dev/null +++ b/ring-android/app/src/main/res/layout/frag_plugin_settings.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.widget.RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/plugin_setting_icon" + android:layout_marginTop="32dp" + android:layout_width="80dp" + android:layout_height="80dp" + android:layout_centerHorizontal="true" + app:srcCompat="@drawable/baseline_group_24" /> + + <TextView + android:id="@+id/plugin_setting_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:layout_below="@+id/plugin_setting_icon" + android:singleLine="true" + android:text="@string/video_plugins" + android:textSize="20sp" + android:layout_centerHorizontal="true" + android:textAppearance="?android:attr/textAppearanceLarge" /> + + <View + android:id="@+id/plugin_setting_divider" + android:layout_below="@+id/plugin_setting_title" + android:layout_marginTop="16dp" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + + <LinearLayout + android:id="@+id/plugin_setting_buttons" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_below="@+id/plugin_setting_divider" + android:layout_marginTop="16dp" + android:weightSum="2"> + +<!-- <RelativeLayout--> +<!-- android:id="@+id/plugin_setting_load"--> +<!-- android:layout_width="wrap_content"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:layout_weight="1"--> +<!-- android:clickable="true">--> + +<!-- <ImageView--> +<!-- android:id="@+id/plugin_setting_load_image"--> +<!-- android:layout_width="32dp"--> +<!-- android:layout_height="32dp"--> +<!-- android:src="@drawable/baseline_delete_24"--> +<!-- android:layout_centerHorizontal="true"/>--> + +<!-- <TextView--> +<!-- android:layout_below="@+id/plugin_setting_load_image"--> +<!-- android:layout_width="wrap_content"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:text="Activate"--> +<!-- android:layout_centerHorizontal="true"/>--> + +<!-- </RelativeLayout>--> + <RelativeLayout + android:id="@+id/plugin_setting_reset" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:clickable="true"> + + <ImageView + android:id="@+id/plugin_setting_reset_image" + android:layout_width="32dp" + android:layout_height="32dp" + android:src="@drawable/round_settings_backup_restore_24" + android:layout_centerHorizontal="true"/> + + <TextView + android:layout_below="@+id/plugin_setting_reset_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Reset" + android:layout_centerHorizontal="true"/> + + </RelativeLayout> + <RelativeLayout + android:id="@+id/plugin_setting_install" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:clickable="true" + android:focusable="true"> + + <ImageView + android:id="@+id/plugin_setting_install_image" + android:layout_width="32dp" + android:layout_height="32dp" + android:src="@drawable/baseline_delete_24" + android:layout_centerHorizontal="true"/> + + <TextView + android:layout_below="@+id/plugin_setting_install_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Uninstall" + android:layout_centerHorizontal="true"/> + + </RelativeLayout> + + </LinearLayout> + + + <View + android:layout_below="@+id/plugin_setting_buttons" + android:layout_marginTop="16dp" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + +</android.widget.RelativeLayout> \ No newline at end of file diff --git a/ring-android/app/src/main/res/layout/frag_plugins_list_item.xml b/ring-android/app/src/main/res/layout/frag_plugins_list_item.xml index ed085facc1d8293ce5ac4d80ce86dd3009d51669..fb8aed7ac94c111a28817a83e018463487a36ec7 100644 --- a/ring-android/app/src/main/res/layout/frag_plugins_list_item.xml +++ b/ring-android/app/src/main/res/layout/frag_plugins_list_item.xml @@ -12,6 +12,7 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" tools:text="My plugin" + android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@+id/plugin_item_icon" app:layout_constraintTop_toTopOf="parent" /> diff --git a/ring-android/app/src/main/res/layout/frag_settings.xml b/ring-android/app/src/main/res/layout/frag_settings.xml index 5993c5d879e9d984f82864af380d94415e5a8185..32550a67e3c9841cf30c7634b50e598af77f085e 100644 --- a/ring-android/app/src/main/res/layout/frag_settings.xml +++ b/ring-android/app/src/main/res/layout/frag_settings.xml @@ -122,6 +122,62 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. </RelativeLayout> + <RelativeLayout + android:id="@+id/settings_plugins_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="@dimen/padding_large"> + + <ImageView + android:id="@+id/plugins_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:contentDescription="@string/pref_plugins_summary" + app:srcCompat="@drawable/ic_plugin" + android:layout_marginEnd="32dp"/> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toStartOf="@+id/settings_plugins_switch" + android:layout_toEndOf="@+id/plugins_image" + android:orientation="vertical"> + <TextView + android:id="@+id/plugins_title" + style="@style/ListPrimary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="3" + android:singleLine="false" + android:text="@string/pref_plugins_title" + android:layout_toEndOf="@id/plugins_image" + android:layout_marginEnd="16dp" /> + + <TextView + style="@style/ListSecondary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/pref_plugins_summary" + android:layout_toEndOf="@id/plugins_image" + android:layout_marginEnd="16dp" + android:layout_below="@id/plugins_title"/> + + </LinearLayout> + + <Switch + android:id="@+id/settings_plugins_switch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:checked="false" + android:layout_marginStart="16dp" + android:layout_centerVertical="true"/> + + </RelativeLayout> + + <!-- Contacts settings --> <!-- @@ -440,62 +496,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. android:layout_below="@id/system_clear_history_title"/> </RelativeLayout> - - <RelativeLayout - android:id="@+id/settings_plugins_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="@dimen/padding_large"> - - <ImageView - android:id="@+id/plugins_image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" - android:contentDescription="@string/pref_plugins_summary" - app:srcCompat="@drawable/ic_plugin" - android:layout_marginEnd="32dp"/> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toStartOf="@+id/settings_plugins_switch" - android:layout_toEndOf="@+id/plugins_image" - android:orientation="vertical"> - <TextView - android:id="@+id/plugins_title" - style="@style/ListPrimary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:maxLines="3" - android:singleLine="false" - android:text="@string/pref_plugins_title" - android:layout_toEndOf="@id/plugins_image" - android:layout_marginEnd="16dp" /> - - <TextView - style="@style/ListSecondary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/pref_plugins_summary" - android:layout_toEndOf="@id/plugins_image" - android:layout_marginEnd="16dp" - android:layout_below="@id/plugins_title"/> - - </LinearLayout> - - <Switch - android:id="@+id/settings_plugins_switch" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:checked="false" - android:layout_marginStart="16dp" - android:layout_centerVertical="true"/> - - </RelativeLayout> - </LinearLayout> </cx.ring.views.BoundedScrollView> </RelativeLayout> diff --git a/ring-android/app/src/main/res/layout/single_button_preference.xml b/ring-android/app/src/main/res/layout/single_button_preference.xml index 2525f630a7afb3baebbb1516e556579d2c257ad7..30d62e0c30788794cd48f1d9acaaf4764647c51e 100644 --- a/ring-android/app/src/main/res/layout/single_button_preference.xml +++ b/ring-android/app/src/main/res/layout/single_button_preference.xml @@ -19,14 +19,15 @@ android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="16dp" + android:layout_marginStart="76dp" android:layout_marginTop="16dp" android:ellipsize="marquee" android:fadingEdge="horizontal" android:singleLine="true" android:text="@string/video_plugins" + android:textSize="16sp" android:textAppearance="?android:attr/textAppearanceLarge" - app:layout_constraintStart_toEndOf="@+id/icon" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView @@ -35,12 +36,13 @@ android:layout_height="wrap_content" android:layout_below="@android:id/title" android:layout_alignStart="@android:id/title" - android:layout_marginStart="16dp" + android:layout_marginStart="76dp" android:maxLines="4" android:text="@string/video_plugins" + android:textSize="14sp" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary" - app:layout_constraintStart_toEndOf="@+id/icon" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/title" /> <Button diff --git a/ring-android/app/src/main/res/values/strings.xml b/ring-android/app/src/main/res/values/strings.xml index 5f7dbf9652267c0a126bd2264e2bebce6d2182f9..d265befe6b7addd3168677d0bbeb73f1367c452a 100644 --- a/ring-android/app/src/main/res/values/strings.xml +++ b/ring-android/app/src/main/res/values/strings.xml @@ -70,6 +70,7 @@ along with this program; if not, write to the Free Software <string name="menu_item_accounts">Manage accounts</string> <string name="menu_item_account">Manage account</string> <string name="menu_item_settings">Settings</string> + <string name="menu_item_plugin_list">Plugins Settings</string> <string name="menu_item_share">Share my contact</string> <string name="menu_item_about">About Jami</string> diff --git a/ring-android/app/src/main/res/values/strings_preferences.xml b/ring-android/app/src/main/res/values/strings_preferences.xml index f0e7d4a06aa89e91aaa18c79517b35dfd3bcec61..4897685d1d5118d89789ed2b8853a73454a75d9f 100644 --- a/ring-android/app/src/main/res/values/strings_preferences.xml +++ b/ring-android/app/src/main/res/values/strings_preferences.xml @@ -41,7 +41,7 @@ <string name="pref_persistNotification_summary">Allow running in background to receive calls and messages.</string> <!-- Plugin --> - <string name="pref_plugins_title">Plugins (experimental)</string> + <string name="pref_plugins_title">Plugins</string> <string name="pref_plugins_summary">Enable plugins and set their parameters</string> <string name="fab_plugins_add">Add a new plugin</string> <string name="plugin_uninstall_title">Uninstall Plugin ?</string> diff --git a/ring-android/libringclient/src/main/java/cx/ring/call/CallView.java b/ring-android/libringclient/src/main/java/cx/ring/call/CallView.java index 757404d27cdf7442490c04109d29b2b2780feba7..79d8386ced9c0dbac5750ea0177e96d86f23cae7 100644 --- a/ring-android/libringclient/src/main/java/cx/ring/call/CallView.java +++ b/ring-android/libringclient/src/main/java/cx/ring/call/CallView.java @@ -32,7 +32,6 @@ public interface CallView { void displayContactBubble(boolean display); void displayVideoSurface(boolean displayVideoSurface, boolean displayPreviewContainer); -// void displayPluginSurface(); void displayPreviewSurface(boolean display); diff --git a/ring-android/libringclient/src/main/java/cx/ring/model/Conference.java b/ring-android/libringclient/src/main/java/cx/ring/model/Conference.java index 484ff5d472b527cba2ca2f7c495e368f2ea7e775..36bd19e9bf2410d17b4e5f050dd01eb8b3176964 100644 --- a/ring-android/libringclient/src/main/java/cx/ring/model/Conference.java +++ b/ring-android/libringclient/src/main/java/cx/ring/model/Conference.java @@ -73,7 +73,7 @@ public class Conference { } public String getPluginId() { - return mId + "_plugin"; + return "local"; } public String getConfId() {