diff options
Diffstat (limited to 'java/src')
3 files changed, 200 insertions, 111 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 6d5304d9..6cf1aef4 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -77,7 +77,6 @@ import android.util.Log;  import android.util.Size;  import android.util.Slog;  import android.util.SparseArray; -import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.view.ViewGroup.LayoutParams; @@ -86,7 +85,6 @@ import android.view.WindowInsets;  import android.view.animation.AlphaAnimation;  import android.view.animation.Animation;  import android.view.animation.LinearInterpolator; -import android.widget.Button;  import android.widget.TextView;  import androidx.annotation.MainThread; @@ -97,7 +95,6 @@ import androidx.viewpager.widget.ViewPager;  import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyState;  import com.android.intentresolver.AbstractMultiProfilePagerAdapter.EmptyStateProvider;  import com.android.intentresolver.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState; -import com.android.intentresolver.ResolverListAdapter.ViewHolder;  import com.android.intentresolver.chooser.DisplayResolveInfo;  import com.android.intentresolver.chooser.MultiDisplayResolveInfo;  import com.android.intentresolver.chooser.TargetInfo; @@ -108,6 +105,7 @@ import com.android.intentresolver.model.AppPredictionServiceResolverComparator;  import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;  import com.android.intentresolver.shortcuts.AppPredictorFactory;  import com.android.intentresolver.shortcuts.ShortcutLoader; +import com.android.intentresolver.widget.ActionRow;  import com.android.intentresolver.widget.ResolverDrawerLayout;  import com.android.internal.annotations.VisibleForTesting;  import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; @@ -629,7 +627,7 @@ public class ChooserActivity extends ResolverActivity implements          updateProfileViewButton();      } -    private void onCopyButtonClicked(View v) { +    private void onCopyButtonClicked() {          Intent targetIntent = getTargetIntent();          if (targetIntent == null) {              finish(); @@ -752,21 +750,23 @@ public class ChooserActivity extends ResolverActivity implements          int previewType = ChooserContentPreviewUi.findPreferredContentPreview(                  targetIntent, getContentResolver(), this::isImageType); -        ChooserContentPreviewUi.ActionButtonFactory buttonFactory = -                new ChooserContentPreviewUi.ActionButtonFactory() { +        ChooserContentPreviewUi.ActionFactory actionFactory = +                new ChooserContentPreviewUi.ActionFactory() {                      @Override -                    public Button createCopyButton() { -                        return ChooserActivity.this.createCopyButton(); +                    public ActionRow.Action createCopyButton() { +                        return ChooserActivity.this.createCopyAction();                      } +                    @Nullable                      @Override -                    public Button createEditButton() { -                        return ChooserActivity.this.createEditButton(targetIntent); +                    public ActionRow.Action createEditButton() { +                        return ChooserActivity.this.createEditAction(targetIntent);                      } +                    @Nullable                      @Override -                    public Button createNearbyButton() { -                        return ChooserActivity.this.createNearbyButton(targetIntent); +                    public ActionRow.Action createNearbyButton() { +                        return ChooserActivity.this.createNearbyAction(targetIntent);                      }                  }; @@ -775,7 +775,7 @@ public class ChooserActivity extends ResolverActivity implements                  targetIntent,                  getResources(),                  getLayoutInflater(), -                buttonFactory, +                actionFactory,                  parent,                  previewCoordinator,                  getContentResolver(), @@ -902,54 +902,46 @@ public class ChooserActivity extends ResolverActivity implements          return dri;      } -    private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) { -        Button b = (Button) LayoutInflater.from(this).inflate(R.layout.chooser_action_button, null); -        if (icon != null) { -            final int size = getResources() -                    .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size); -            icon.setBounds(0, 0, size, size); -            b.setCompoundDrawablesRelative(icon, null, null, null); -        } -        b.setText(title); -        b.setOnClickListener(r); -        return b; -    } - -    private Button createCopyButton() { -        final Button b = createActionButton( +    private ActionRow.Action createCopyAction() { +        return new ActionRow.Action( +                com.android.internal.R.id.chooser_copy_button, +                getString(com.android.internal.R.string.copy),                  getDrawable(com.android.internal.R.drawable.ic_menu_copy_material), -                getString(com.android.internal.R.string.copy), this::onCopyButtonClicked); -        b.setId(com.android.internal.R.id.chooser_copy_button); -        return b; +                this::onCopyButtonClicked);      } -    private @Nullable Button createNearbyButton(Intent originalIntent) { +    @Nullable +    private ActionRow.Action createNearbyAction(Intent originalIntent) {          final TargetInfo ti = getNearbySharingTarget(originalIntent); -        if (ti == null) return null; +        if (ti == null) { +            return null; +        } -        final Button b = createActionButton( -                ti.getDisplayIconHolder().getDisplayIcon(), +        return new ActionRow.Action( +                com.android.internal.R.id.chooser_nearby_button,                  ti.getDisplayLabel(), -                (View unused) -> { +                ti.getDisplayIconHolder().getDisplayIcon(), +                () -> {                      getChooserActivityLogger().logActionSelected(                              ChooserActivityLogger.SELECTION_TYPE_NEARBY);                      // Action bar is user-independent, always start as primary                      safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());                      finish(); -                } -        ); -        b.setId(com.android.internal.R.id.chooser_nearby_button); -        return b; +                });      } -    private @Nullable Button createEditButton(Intent originalIntent) { +    @Nullable +    private ActionRow.Action createEditAction(Intent originalIntent) {          final TargetInfo ti = getEditSharingTarget(originalIntent); -        if (ti == null) return null; +        if (ti == null) { +            return null; +        } -        final Button b = createActionButton( -                ti.getDisplayIconHolder().getDisplayIcon(), +        return new ActionRow.Action( +                com.android.internal.R.id.chooser_edit_button,                  ti.getDisplayLabel(), -                (View unused) -> { +                ti.getDisplayIconHolder().getDisplayIcon(), +                () -> {                      // Log share completion via edit                      getChooserActivityLogger().logActionSelected(                              ChooserActivityLogger.SELECTION_TYPE_EDIT); @@ -967,8 +959,6 @@ public class ChooserActivity extends ResolverActivity implements                      }                  }          ); -        b.setId(com.android.internal.R.id.chooser_edit_button); -        return b;      }      @Nullable @@ -977,17 +967,6 @@ public class ChooserActivity extends ResolverActivity implements          return firstImage != null && firstImage.isVisibleToUser() ? firstImage : null;      } -    private void addActionButton(ViewGroup parent, Button b) { -        if (b == null) return; -        final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( -                        LayoutParams.WRAP_CONTENT, -                        LayoutParams.WRAP_CONTENT -                ); -        final int gap = getResources().getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2; -        lp.setMarginsRelative(gap, 0, gap, 0); -        parent.addView(b, lp); -    } -      /**       * Wrapping the ContentResolver call to expose for easier mocking,       * and to avoid mocking Android core classes. diff --git a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java index 22ff55db..f9f4ee98 100644 --- a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java +++ b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java @@ -34,11 +34,12 @@ import android.util.PluralsMessageFormatter;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.widget.Button;  import android.widget.ImageView;  import android.widget.TextView; +import androidx.annotation.Nullable; + +import com.android.intentresolver.widget.ActionRow;  import com.android.intentresolver.widget.RoundedRectImageView;  import com.android.internal.annotations.VisibleForTesting; @@ -88,15 +89,17 @@ public final class ChooserContentPreviewUi {       * they're determined to be appropriate for the particular preview we display.       * TODO: clarify why action buttons are part of preview logic.       */ -    public interface ActionButtonFactory { -        /** Create a button that copies the share content to the clipboard. */ -        Button createCopyButton(); +    public interface ActionFactory { +        /** Create an action that copies the share content to the clipboard. */ +        ActionRow.Action createCopyButton(); -        /** Create a button that opens the share content in a system-default editor. */ -        Button createEditButton(); +        /** Create an action that opens the share content in a system-default editor. */ +        @Nullable +        ActionRow.Action createEditButton(); -        /** Create a "Share to Nearby" button. */ -        Button createNearbyButton(); +        /** Create an "Share to Nearby" action. */ +        @Nullable +        ActionRow.Action createNearbyButton();      }      /** @@ -173,7 +176,7 @@ public final class ChooserContentPreviewUi {              Intent targetIntent,              Resources resources,              LayoutInflater layoutInflater, -            ActionButtonFactory buttonFactory, +            ActionFactory actionFactory,              ViewGroup parent,              ContentPreviewCoordinator previewCoord,              ContentResolver contentResolver, @@ -184,18 +187,16 @@ public final class ChooserContentPreviewUi {              case CONTENT_PREVIEW_TEXT:                  layout = displayTextContentPreview(                          targetIntent, -                        resources,                          layoutInflater, -                        buttonFactory, +                        createTextPreviewActions(actionFactory),                          parent,                          previewCoord);                  break;              case CONTENT_PREVIEW_IMAGE:                  layout = displayImageContentPreview(                          targetIntent, -                        resources,                          layoutInflater, -                        buttonFactory, +                        createImagePreviewActions(actionFactory),                          parent,                          previewCoord,                          contentResolver, @@ -206,7 +207,7 @@ public final class ChooserContentPreviewUi {                          targetIntent,                          resources,                          layoutInflater, -                        buttonFactory, +                        createFilePreviewActions(actionFactory),                          parent,                          previewCoord,                          contentResolver); @@ -235,20 +236,18 @@ public final class ChooserContentPreviewUi {      private static ViewGroup displayTextContentPreview(              Intent targetIntent, -            Resources resources,              LayoutInflater layoutInflater, -            ActionButtonFactory buttonFactory, +            List<ActionRow.Action> actions,              ViewGroup parent,              ContentPreviewCoordinator previewCoord) {          ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(                  R.layout.chooser_grid_preview_text, parent, false); -        final ViewGroup actionRow = -                (ViewGroup) contentPreviewLayout.findViewById( -                        com.android.internal.R.id.chooser_action_row); -        final int iconMargin = resources.getDimensionPixelSize(R.dimen.resolver_icon_margin); -        addActionButton(actionRow, buttonFactory.createCopyButton(), iconMargin); -        addActionButton(actionRow, buttonFactory.createNearbyButton(), iconMargin); +        final ActionRow actionRow = +                contentPreviewLayout.findViewById(com.android.internal.R.id.chooser_action_row); +        if (actionRow != null) { +            actionRow.setActions(actions); +        }          CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);          if (sharingText == null) { @@ -296,11 +295,20 @@ public final class ChooserContentPreviewUi {          return contentPreviewLayout;      } +    private static List<ActionRow.Action> createTextPreviewActions(ActionFactory actionFactory) { +        ArrayList<ActionRow.Action> actions = new ArrayList<>(2); +        actions.add(actionFactory.createCopyButton()); +        ActionRow.Action nearbyAction = actionFactory.createNearbyButton(); +        if (nearbyAction != null) { +            actions.add(nearbyAction); +        } +        return actions; +    } +      private static ViewGroup displayImageContentPreview(              Intent targetIntent, -            Resources resources,              LayoutInflater layoutInflater, -            ActionButtonFactory buttonFactory, +            List<ActionRow.Action> actions,              ViewGroup parent,              ContentPreviewCoordinator previewCoord,              ContentResolver contentResolver, @@ -310,13 +318,11 @@ public final class ChooserContentPreviewUi {          ViewGroup imagePreview = contentPreviewLayout.findViewById(                  com.android.internal.R.id.content_preview_image_area); -        final ViewGroup actionRow = -                (ViewGroup) contentPreviewLayout.findViewById( -                        com.android.internal.R.id.chooser_action_row); -        final int iconMargin = resources.getDimensionPixelSize(R.dimen.resolver_icon_margin); -        //TODO: addActionButton(actionRow, buttonFactory.createCopyButton(), iconMargin); -        addActionButton(actionRow, buttonFactory.createNearbyButton(), iconMargin); -        addActionButton(actionRow, buttonFactory.createEditButton(), iconMargin); +        final ActionRow actionRow = +                contentPreviewLayout.findViewById(com.android.internal.R.id.chooser_action_row); +        if (actionRow != null) { +            actionRow.setActions(actions); +        }          String action = targetIntent.getAction();          if (Intent.ACTION_SEND.equals(action)) { @@ -375,24 +381,37 @@ public final class ChooserContentPreviewUi {          return contentPreviewLayout;      } +    private static List<ActionRow.Action> createImagePreviewActions( +            ActionFactory buttonFactory) { +        ArrayList<ActionRow.Action> actions = new ArrayList<>(2); +        //TODO: add copy action; +        ActionRow.Action action = buttonFactory.createNearbyButton(); +        if (action != null) { +            actions.add(action); +        } +        action = buttonFactory.createEditButton(); +        if (action != null) { +            actions.add(action); +        } +        return actions; +    } +      private static ViewGroup displayFileContentPreview(              Intent targetIntent,              Resources resources,              LayoutInflater layoutInflater, -            ActionButtonFactory buttonFactory, +            List<ActionRow.Action> actions,              ViewGroup parent,              ContentPreviewCoordinator previewCoord,              ContentResolver contentResolver) {          ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(                  R.layout.chooser_grid_preview_file, parent, false); -        final ViewGroup actionRow = -                (ViewGroup) contentPreviewLayout.findViewById( -                        com.android.internal.R.id.chooser_action_row); -        final int iconMargin = resources.getDimensionPixelSize(R.dimen.resolver_icon_margin); -        //TODO(b/120417119): -        // addActionButton(actionRow, buttonFactory.createCopyButton(), iconMargin); -        addActionButton(actionRow, buttonFactory.createNearbyButton(), iconMargin); +        final ActionRow actionRow = +                contentPreviewLayout.findViewById(com.android.internal.R.id.chooser_action_row); +        if (actionRow != null) { +            actionRow.setActions(actions); +        }          String action = targetIntent.getAction();          if (Intent.ACTION_SEND.equals(action)) { @@ -438,6 +457,17 @@ public final class ChooserContentPreviewUi {          return contentPreviewLayout;      } +    private static List<ActionRow.Action> createFilePreviewActions(ActionFactory actionFactory) { +        List<ActionRow.Action> actions = new ArrayList<>(1); +        //TODO(b/120417119): +        // add action buttonFactory.createCopyButton() +        ActionRow.Action action = actionFactory.createNearbyButton(); +        if (action != null) { +            actions.add(action); +        } +        return actions; +    } +      private static void logContentPreviewWarning(Uri uri) {          // The ContentResolver already logs the exception. Log something more informative.          Log.w(TAG, "Could not load (" + uri.toString() + ") thumbnail/name for preview. If " @@ -475,19 +505,6 @@ public final class ChooserContentPreviewUi {          }      } -    private static void addActionButton(ViewGroup parent, Button b, int iconMargin) { -        if (b == null) { -            return; -        } -        final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( -                        LayoutParams.WRAP_CONTENT, -                        LayoutParams.WRAP_CONTENT -                ); -        final int gap = iconMargin / 2; -        lp.setMarginsRelative(gap, 0, gap, 0); -        parent.addView(b, lp); -    } -      private static FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {          String fileName = null;          boolean hasThumbnail = false; diff --git a/java/src/com/android/intentresolver/widget/ActionRow.kt b/java/src/com/android/intentresolver/widget/ActionRow.kt new file mode 100644 index 00000000..1be48f34 --- /dev/null +++ b/java/src/com/android/intentresolver/widget/ActionRow.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.intentresolver.widget + +import android.annotation.LayoutRes +import android.content.Context +import android.content.res.Resources.ID_NULL +import android.graphics.drawable.Drawable +import android.os.Parcelable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.Button +import android.widget.LinearLayout +import com.android.intentresolver.R + +// TODO: extract an interface out of the class, use it in layout hierarchy an have a layout inflater +//  to instantiate the right view based on a flag value. +class ActionRow : LinearLayout { +    constructor(context: Context) : this(context, null) +    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) +    constructor( +        context: Context, attrs: AttributeSet?, defStyleAttr: Int +    ) : this(context, attrs, defStyleAttr, 0) + +    constructor( +        context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int +    ) : super(context, attrs, defStyleAttr, defStyleRes) { +        orientation = HORIZONTAL +    } + +    @LayoutRes +    private val itemLayout = R.layout.chooser_action_button +    private val itemMargin = +        context.resources.getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2 +    private var actions: List<Action> = emptyList() + +    override fun onRestoreInstanceState(state: Parcelable?) { +        super.onRestoreInstanceState(state) +        setActions(actions) +    } + +    fun setActions(actions: List<Action>) { +        removeAllViews() +        this.actions = ArrayList(actions) +        for (action in actions) { +            addAction(action) +        } +    } + +    private fun addAction(action: Action) { +        val b = LayoutInflater.from(context).inflate(itemLayout, null) as Button +        if (action.icon != null) { +            val size = resources +                .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size) +            action.icon.setBounds(0, 0, size, size) +            b.setCompoundDrawablesRelative(action.icon, null, null, null) +        } +        b.text = action.label ?: "" +        b.setOnClickListener { +            action.onClicked.run() +        } +        b.id = action.id +        addView(b) +    } + +    override fun generateDefaultLayoutParams(): LayoutParams = +        super.generateDefaultLayoutParams().apply { +            setMarginsRelative(itemMargin, 0, itemMargin, 0) +        } + +    class Action @JvmOverloads constructor( +        // TODO: apparently, IDs set to this field are used in unit tests only; evaluate whether we +        //  get rid of them +        val id: Int = ID_NULL, +        val label: CharSequence?, +        val icon: Drawable?, +        val onClicked: Runnable, +    ) +}  |