diff --git a/app/src/main/java/fr/neamar/kiss/PickAppWidgetActivity.java b/app/src/main/java/fr/neamar/kiss/PickAppWidgetActivity.java index c11fb3a25..405de6aa3 100644 --- a/app/src/main/java/fr/neamar/kiss/PickAppWidgetActivity.java +++ b/app/src/main/java/fr/neamar/kiss/PickAppWidgetActivity.java @@ -1,5 +1,7 @@ package fr.neamar.kiss; +import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER; + import android.app.Activity; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -126,35 +128,44 @@ private static List getWidgetList(@NonNull Context context) { List infoList = new ArrayList<>(installedProviders.size()); PackageManager packageManager = context.getPackageManager(); for (AppWidgetProviderInfo providerInfo : installedProviders) { - // get widget name - String label = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - label = providerInfo.loadLabel(packageManager); - } - if (label == null) { - label = providerInfo.label; - } + if (!isHiddenFromPicker(providerInfo)) { + // get widget name + String label = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + label = providerInfo.loadLabel(packageManager); + } + if (label == null) { + label = providerInfo.label; + } - // get widget description - String description = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - CharSequence desc = providerInfo.loadDescription(context); - if (desc != null) - description = desc.toString(); - } + // get widget description + String description = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + CharSequence desc = providerInfo.loadDescription(context); + if (desc != null) + description = desc.toString(); + } - String appName = providerInfo.provider.getPackageName(); - try { - ApplicationInfo appInfo = packageManager.getApplicationInfo(providerInfo.provider.getPackageName(), 0); - appName = appInfo.loadLabel(packageManager).toString(); - } catch (Exception e) { - Log.e(TAG, "get `" + providerInfo.provider.getPackageName() + "` label"); + String appName = providerInfo.provider.getPackageName(); + try { + ApplicationInfo appInfo = packageManager.getApplicationInfo(providerInfo.provider.getPackageName(), 0); + appName = appInfo.loadLabel(packageManager).toString(); + } catch (Exception e) { + Log.e(TAG, "get `" + providerInfo.provider.getPackageName() + "` label"); + } + infoList.add(new WidgetInfo(appName, label, description, providerInfo)); } - infoList.add(new WidgetInfo(appName, label, description, providerInfo)); } return infoList; } + private static boolean isHiddenFromPicker(AppWidgetProviderInfo providerInfo) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + return (providerInfo.widgetFeatures & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0; + } + return false; + } + @WorkerThread private static Drawable getWidgetPreview(@NonNull Context context, @NonNull AppWidgetProviderInfo info) { Drawable preview = null; @@ -219,7 +230,7 @@ private WidgetInfo(String app, String name, String description, AppWidgetProvide } } - private interface MenuItem { + public interface MenuItem { @NonNull String getName(); } diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Widgets.java b/app/src/main/java/fr/neamar/kiss/forwarder/Widgets.java index 7b8efd5a5..8e2f20aba 100644 --- a/app/src/main/java/fr/neamar/kiss/forwarder/Widgets.java +++ b/app/src/main/java/fr/neamar/kiss/forwarder/Widgets.java @@ -1,5 +1,8 @@ package fr.neamar.kiss.forwarder; +import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL; +import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE; + import android.app.Activity; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; @@ -43,6 +46,7 @@ class Widgets extends Forwarder { private static final int REQUEST_APPWIDGET_PICKED = 9; private static final int REQUEST_APPWIDGET_BOUND = 11; private static final int REQUEST_APPWIDGET_CONFIGURED = 5; + private static final int REQUEST_APPWIDGET_RECONFIGURED = 13; private static final int APPWIDGET_HOST_ID = 442; @@ -82,11 +86,12 @@ void onActivityResult(int requestCode, int resultCode, Intent data) { case Activity.RESULT_OK: switch (requestCode) { case REQUEST_APPWIDGET_CONFIGURED: + case REQUEST_APPWIDGET_RECONFIGURED: Log.i(TAG, "Widget configured"); break; case REQUEST_APPWIDGET_BOUND: if (data != null) { - configureAppWidget(data); + addAppWidget(data); } else { Log.i(TAG, "Widget bind failed"); } @@ -100,7 +105,7 @@ void onActivityResult(int requestCode, int resultCode, Intent data) { } } // if binding not required we can continue with adding the widget - configureAppWidget(data); + addAppWidget(data); } else { Log.i(TAG, "Widget picker failed"); } @@ -113,8 +118,8 @@ void onActivityResult(int requestCode, int resultCode, Intent data) { requestCode == REQUEST_APPWIDGET_PICKED) && data != null) { // if widget was not selected, delete it - int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - if (appWidgetId != -1) { + int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { // find widget views for appWidgetId List viewsToRemove = new ArrayList<>(); for (int i = 0; i < widgetArea.getChildCount(); i++) { @@ -250,9 +255,8 @@ private void addWidget(int appWidgetId, int lineSize) { ListPopup popupMenu = new ListPopup(mainActivity); popupMenu.setAdapter(popupMenuAdapter); popupMenu.setOnItemClickListener((adapter1, view, position) -> { - popupMenu.dismiss(); @StringRes int stringId = ((ListPopup.Item) adapter1.getItem(position)).stringId; - popupMenuClickHandler(view.getContext(), stringId, widgetWithMenuCurrentlyDisplayed); + popupMenuClickHandler(stringId, widgetWithMenuCurrentlyDisplayed); }); mainActivity.registerPopup(popupMenu); popupMenu.show(hostView); @@ -267,6 +271,9 @@ private void addWidget(int appWidgetId, int lineSize) { private void buildPopupMenu(Context context, ArrayAdapter adapter, AppWidgetProviderInfo currentAppWidgetInfo, AppWidgetHostView widgetWithMenuCurrentlyDisplayed) { final ViewGroup parent = (ViewGroup) widgetWithMenuCurrentlyDisplayed.getParent(); + if (isReconfigurable(currentAppWidgetInfo)) { + adapter.add(new ListPopup.Item(context, R.string.menu_widget_settings)); + } int increasedLineHeight = getIncreasedLineHeight(widgetWithMenuCurrentlyDisplayed); if (!preventIncreaseLineHeight(increasedLineHeight, currentAppWidgetInfo)) { adapter.add(new ListPopup.Item(context, R.string.menu_size_up)); @@ -284,9 +291,11 @@ private void buildPopupMenu(Context context, ArrayAdapter adapte adapter.add(new ListPopup.Item(context, R.string.menu_widget_remove)); } - private void popupMenuClickHandler(Context context, @StringRes int stringId, AppWidgetHostView widgetWithMenuCurrentlyDisplayed) { + private void popupMenuClickHandler(@StringRes int stringId, AppWidgetHostView widgetWithMenuCurrentlyDisplayed) { final ViewGroup parent = (ViewGroup) widgetWithMenuCurrentlyDisplayed.getParent(); - if (stringId == R.string.menu_widget_remove) { + if (stringId == R.string.menu_widget_settings) { + reConfigureAppWidget(widgetWithMenuCurrentlyDisplayed.getAppWidgetId()); + } else if (stringId == R.string.menu_widget_remove) { parent.removeView(widgetWithMenuCurrentlyDisplayed); mAppWidgetHost.deleteAppWidgetId(widgetWithMenuCurrentlyDisplayed.getAppWidgetId()); serializeState(); @@ -337,7 +346,7 @@ private int getIncreasedLineHeight(AppWidgetHostView hostView) { * @param hostView host view for widget * @param height height of widget */ - private void setWidgetSize(AppWidgetHostView hostView, int height, AppWidgetProviderInfo appWidgetInfo) { + private void setWidgetSize(AppWidgetHostView hostView, int height, @NonNull AppWidgetProviderInfo appWidgetInfo) { hostView.setMinimumHeight(height); hostView.setMinimumWidth(Math.min(appWidgetInfo.minWidth, appWidgetInfo.minResizeWidth)); ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height); @@ -388,12 +397,10 @@ private boolean preventIncreaseLineHeight(int height, AppWidgetProviderInfo appW /** * Adds widget to Activity and persists it in prefs to be able to restore it * - * @param data Intent holding widget id to add + * @param appWidgetId id of widget to add + * @param appWidgetInfo */ - private void addAppWidget(Intent data) { - int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); - + private void addAppWidget(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) { // calculate already used lines int usedLines = 0; for (int i = 0; i < widgetArea.getChildCount(); i++) { @@ -413,7 +420,7 @@ private void addAppWidget(Intent data) { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) private static void requestBindWidget(@NonNull Activity activity, @NonNull Intent data) { - final int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0); + final int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); final ComponentName provider = data.getParcelableExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER); final UserHandle profile; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { @@ -442,24 +449,34 @@ private static void requestBindWidget(@NonNull Activity activity, @NonNull Inten * * @param data Intent holding widget id to configure */ - private void configureAppWidget(Intent data) { - int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - + private void addAppWidget(Intent data) { + int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); + if (appWidgetInfo != null) { + // Add the widget + addAppWidget(appWidgetId, appWidgetInfo); + + if (!isConfigurationOptional(appWidgetInfo)) { + configureAppWidget(appWidgetId, appWidgetInfo, REQUEST_APPWIDGET_CONFIGURED); + } + } + } - // Add the widget - addAppWidget(data); + private void reConfigureAppWidget(int appWidgetId) { + AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); + configureAppWidget(appWidgetId, appWidgetInfo, REQUEST_APPWIDGET_RECONFIGURED); + } - if (appWidgetInfo.configure != null) { - // Launch over to configure widget, if needed. + private void configureAppWidget(int appWidgetId, AppWidgetProviderInfo appWidgetInfo, int requestCode) { + if (appWidgetInfo != null && appWidgetInfo.configure != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mAppWidgetHost.startAppWidgetConfigureActivityForResult(mainActivity, appWidgetId, 0, REQUEST_APPWIDGET_CONFIGURED, null); + mAppWidgetHost.startAppWidgetConfigureActivityForResult(mainActivity, appWidgetId, 0, requestCode, null); } else { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); intent.setComponent(appWidgetInfo.configure); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); try { - mainActivity.startActivityForResult(intent, REQUEST_APPWIDGET_CONFIGURED); + mainActivity.startActivityForResult(intent, requestCode); } catch (SecurityException e) { Toast.makeText(mainActivity, "KISS doesn't have permission to setup this widget. Believe this is a bug? Please open an issue at https://github.com/Neamar/KISS/issues", Toast.LENGTH_LONG).show(); } @@ -467,6 +484,38 @@ private void configureAppWidget(Intent data) { } } + /** + * A widget's configuration is optional only if it's configuration is marked as optional AND + * it can be reconfigured later. + * + * @param appWidgetInfo + * @return true, if configuration is optional + */ + private boolean isConfigurationOptional(@NonNull AppWidgetProviderInfo appWidgetInfo) { + if (!isReconfigurable(appWidgetInfo)) { + return false; + } + if (appWidgetInfo.configure != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + int featureFlags = appWidgetInfo.widgetFeatures; + return (featureFlags & WIDGET_FEATURE_CONFIGURATION_OPTIONAL) != 0; + } else { + return false; + } + } + + /** + * @param appWidgetInfo + * @return true, if widget can be reconfigured + */ + private boolean isReconfigurable(@NonNull AppWidgetProviderInfo appWidgetInfo) { + if (appWidgetInfo.configure != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + int featureFlags = appWidgetInfo.widgetFeatures; + return (featureFlags & WIDGET_FEATURE_RECONFIGURABLE) != 0; + } else { + return false; + } + } + /** * @param view * @return calculated line size of given view diff --git a/app/src/main/java/fr/neamar/kiss/ui/ListPopup.java b/app/src/main/java/fr/neamar/kiss/ui/ListPopup.java index ae9283649..6116d971f 100644 --- a/app/src/main/java/fr/neamar/kiss/ui/ListPopup.java +++ b/app/src/main/java/fr/neamar/kiss/ui/ListPopup.java @@ -34,27 +34,21 @@ public ListPopup(Context context) { setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); mItemClickListener = null; - mClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - if (dismissOnClick) - dismiss(); - if (mItemClickListener != null) { - LinearLayout layout = getLinearLayout(); - int position = layout.indexOfChild(v); - mItemClickListener.onItemClick(mAdapter, v, position); - } + mClickListener = view -> { + if (dismissOnClick) + dismiss(); + if (mItemClickListener != null) { + LinearLayout layout2 = getLinearLayout(); + int position = layout2.indexOfChild(view); + mItemClickListener.onItemClick(mAdapter, view, position); } }; - mLongClickListener = new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mItemLongClickListener == null) - return false; - LinearLayout layout = getLinearLayout(); - int position = layout.indexOfChild(v); - return mItemLongClickListener.onItemLongClick(mAdapter, v, position); - } + mLongClickListener = view -> { + if (mItemLongClickListener == null) + return false; + LinearLayout layout1 = getLinearLayout(); + int position = layout1.indexOfChild(view); + return mItemLongClickListener.onItemLongClick(mAdapter, view, position); }; } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 118a946e7..0b3700f12 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -132,6 +132,7 @@ Größere Suchleiste Widget hinzufügen Widget entfernen + Einstellungen Touch-Eingaben an Hintergrund weiterleiten Bewegungen emulieren Mehrere Touch-Ereignisse hintereinander senden diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d8316d8ee..9366d032f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -244,6 +244,7 @@ Enlarge Move up Move down + Settings Lock screen from KISS Launcher Action on swipe up Action on swipe down