diff options
9 files changed, 153 insertions, 51 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 43d28761..b712788c 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -84,6 +84,7 @@ import android.view.WindowManager;  import android.widget.FrameLayout;  import android.widget.ImageView;  import android.widget.TabHost; +import android.widget.TabWidget;  import android.widget.TextView;  import android.widget.Toast; @@ -218,9 +219,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      private boolean mRegistered;      private PackageMonitor mPersonalPackageMonitor;      private PackageMonitor mWorkPackageMonitor; -    protected View mProfileView;      protected ResolverDrawerLayout mResolverDrawerLayout; +    private TabHost mTabHost; +    private ResolverViewPager mViewPager;      protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;      protected final LatencyTracker mLatencyTracker = getLatencyTracker(); @@ -305,8 +307,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      private final EnterTransitionAnimationDelegate mEnterTransitionAnimationDelegate =              new EnterTransitionAnimationDelegate(this, () -> mResolverDrawerLayout); -    private final View mContentView = null; -      private final Map<Integer, ProfileRecord> mProfileRecords = new HashMap<>();      private boolean mExcludeSharedText = false; @@ -345,6 +345,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements          mChooserHelper.setInitializer(this::initialize);          if (mChooserServiceFeatureFlags.chooserPayloadToggling()) {              mChooserHelper.setOnChooserRequestChanged(this::onChooserRequestChanged); +            mChooserHelper.setOnPendingSelection(this::onPendingSelection);          }      } @@ -406,9 +407,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      @Override      protected final void onSaveInstanceState(Bundle outState) {          super.onSaveInstanceState(outState); -        ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); -        if (viewPager != null) { -            outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem()); +        if (mViewPager != null) { +            outState.putInt(LAST_SHOWN_TAB_KEY, mViewPager.getCurrentItem());          }      } @@ -648,7 +648,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      }      private void onChooserRequestChanged(ChooserRequest chooserRequest) { -        // intentional reference comarison +        // intentional reference comparison          if (mRequest == chooserRequest) {              return;          } @@ -658,6 +658,23 @@ public class ChooserActivity extends Hilt_ChooserActivity implements          mChooserContentPreviewUi.updateModifyShareAction();          if (recreateAdapters) {              recreatePagerAdapter(); +        } else { +            setTabsViewEnabled(true); +        } +    } + +    private void onPendingSelection() { +        setTabsViewEnabled(false); +    } + +    private void onAppTargetsLoaded(ResolverListAdapter listAdapter) { +        if (mChooserMultiProfilePagerAdapter == null) { +            return; +        } +        if (!isProfilePagerAdapterAttached() +                && listAdapter == mChooserMultiProfilePagerAdapter.getActiveListAdapter()) { +            mChooserMultiProfilePagerAdapter.setupViewPager(mViewPager); +            setTabsViewEnabled(true);          }      } @@ -698,9 +715,12 @@ public class ChooserActivity extends Hilt_ChooserActivity implements                  mRequest.getShareTargetFilter()          ); +        int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();          if (mChooserMultiProfilePagerAdapter != null) {              mChooserMultiProfilePagerAdapter.destroy();          } +        // Update the pager adapter but do not attach it to the view till the targets are reloaded, +        // see onChooserAppTargetsLoaded method.          mChooserMultiProfilePagerAdapter = createMultiProfilePagerAdapter(                  /* context = */ this,                  mProfilePagerResources, @@ -710,8 +730,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements                  mRequest.getInitialIntents(),                  mMaxTargetsPerRow,                  mFeatureFlags); -        mChooserMultiProfilePagerAdapter.setupViewPager( -                requireViewById(com.android.internal.R.id.profile_pager)); +        mChooserMultiProfilePagerAdapter.setCurrentPage(currentPage);          if (mPersonalPackageMonitor != null) {              mPersonalPackageMonitor.unregister();          } @@ -736,15 +755,25 @@ public class ChooserActivity extends Hilt_ChooserActivity implements          }          postRebuildList(                  mChooserMultiProfilePagerAdapter.rebuildTabs( -                    mProfiles.getWorkProfilePresent() -                            || mProfiles.getPrivateProfilePresent())); +                    mProfiles.getWorkProfilePresent() || mProfiles.getPrivateProfilePresent())); +        setTabsViewEnabled(false); +    } + +    private void setTabsViewEnabled(boolean isEnabled) { +        TabWidget tabs = mTabHost.getTabWidget(); +        if (tabs != null) { +            tabs.setEnabled(isEnabled); +        } +        View tabContent = mTabHost.findViewById(com.android.internal.R.id.profile_pager); +        if (tabContent != null) { +            tabContent.setEnabled(isEnabled); +        }      }      @Override      protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { -        ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); -        if (viewPager != null) { -            viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY)); +        if (mViewPager != null) { +            mViewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));          }          mChooserMultiProfilePagerAdapter.clearInactiveProfileCache();      } @@ -1167,8 +1196,9 @@ public class ChooserActivity extends Hilt_ChooserActivity implements                  : R.layout.chooser_grid;          setContentView(mLayoutId); -        mChooserMultiProfilePagerAdapter.setupViewPager( -                requireViewById(com.android.internal.R.id.profile_pager)); +        mTabHost = findViewById(com.android.internal.R.id.profile_tabhost); +        mViewPager = requireViewById(com.android.internal.R.id.profile_pager); +        mChooserMultiProfilePagerAdapter.setupViewPager(mViewPager);          boolean result = postRebuildList(rebuildCompleted);          Trace.endSection();          return result; @@ -1235,16 +1265,13 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      }      private void setupProfileTabs() { -        TabHost tabHost = findViewById(com.android.internal.R.id.profile_tabhost); -        ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); -          mChooserMultiProfilePagerAdapter.setupProfileTabs(                  getLayoutInflater(), -                tabHost, -                viewPager, +                mTabHost, +                mViewPager,                  R.layout.resolver_profile_tab_button,                  com.android.internal.R.id.profile_pager, -                () -> onProfileTabSelected(viewPager.getCurrentItem()), +                () -> onProfileTabSelected(mViewPager.getCurrentItem()),                  new OnProfileSelectedListener() {                      @Override                      public void onProfilePageSelected(@ProfileType int profileId, int pageNumber) {} @@ -1255,8 +1282,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements                      }                  });          mOnSwitchOnWorkSelectedListener = () -> { -            View workTab = tabHost.getTabWidget().getChildAt( -                            mChooserMultiProfilePagerAdapter.getPageNumberForProfile(PROFILE_WORK)); +            View workTab = mTabHost.getTabWidget().getChildAt( +                    mChooserMultiProfilePagerAdapter.getPageNumberForProfile(PROFILE_WORK));              workTab.setFocusable(true);              workTab.setFocusableInTouchMode(true);              workTab.requestFocus(); @@ -1486,9 +1513,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements              mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,                      mSystemWindowInsets.right, 0);          } -        ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); -        if (viewPager.isLayoutRtl()) { -            mChooserMultiProfilePagerAdapter.setupViewPager(viewPager); +        if (mViewPager.isLayoutRtl()) { +            mChooserMultiProfilePagerAdapter.setupViewPager(mViewPager);          }          mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation); @@ -2151,7 +2177,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements       */      private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,              int oldTop, int oldRight, int oldBottom) { -        if (mChooserMultiProfilePagerAdapter == null) { +        if (mChooserMultiProfilePagerAdapter == null || !isProfilePagerAdapterAttached()) {              return;          }          RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView(); @@ -2268,6 +2294,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements          return Math.min(offset, bottom - top);      } +    private boolean isProfilePagerAdapterAttached() { +        return mChooserMultiProfilePagerAdapter == mViewPager.getAdapter(); +    } +      /**       * If we have a tabbed view and are showing 1 row in the current profile and an empty       * state screen in another profile, to prevent cropping of the empty state screen we show @@ -2295,9 +2325,17 @@ public class ChooserActivity extends Hilt_ChooserActivity implements          //TODO: move this block inside ChooserListAdapter (should be called when          // ResolverListAdapter#mPostListReadyRunnable is executed.          if (chooserListAdapter.getDisplayResolveInfoCount() == 0) { +            if (rebuildComplete && mChooserServiceFeatureFlags.chooserPayloadToggling()) { +                onAppTargetsLoaded(listAdapter); +            }              chooserListAdapter.notifyDataSetChanged();          } else { -            chooserListAdapter.updateAlphabeticalList(); +            if (mChooserServiceFeatureFlags.chooserPayloadToggling()) { +                chooserListAdapter.updateAlphabeticalList( +                        () -> onAppTargetsLoaded(listAdapter)); +            } else { +                chooserListAdapter.updateAlphabeticalList(); +            }          }          if (rebuildComplete) { @@ -2541,8 +2579,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements      }      private void setHorizontalScrollingEnabled(boolean enabled) { -        ResolverViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager); -        viewPager.setSwipingEnabled(enabled); +        mViewPager.setSwipingEnabled(enabled);      }      private void setVerticalScrollEnabled(boolean enabled) { diff --git a/java/src/com/android/intentresolver/ChooserHelper.kt b/java/src/com/android/intentresolver/ChooserHelper.kt index 25c2b40f..6317ee1d 100644 --- a/java/src/com/android/intentresolver/ChooserHelper.kt +++ b/java/src/com/android/intentresolver/ChooserHelper.kt @@ -28,9 +28,8 @@ import androidx.lifecycle.lifecycleScope  import androidx.lifecycle.repeatOnLifecycle  import com.android.intentresolver.annotation.JavaInterop  import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ActivityResultRepository +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository  import com.android.intentresolver.data.model.ChooserRequest -import com.android.intentresolver.domain.interactor.UserInteractor -import com.android.intentresolver.inject.Background  import com.android.intentresolver.ui.viewmodel.ChooserViewModel  import com.android.intentresolver.validation.Invalid  import com.android.intentresolver.validation.Valid @@ -38,9 +37,13 @@ import com.android.intentresolver.validation.log  import dagger.hilt.android.scopes.ActivityScoped  import java.util.function.Consumer  import javax.inject.Inject -import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter  import kotlinx.coroutines.flow.filterNotNull  import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach  import kotlinx.coroutines.launch  private const val TAG: String = "ChooserHelper" @@ -79,17 +82,19 @@ class ChooserHelper  @Inject  constructor(      hostActivity: Activity, -    private val userInteractor: UserInteractor,      private val activityResultRepo: ActivityResultRepository, -    @Background private val background: CoroutineDispatcher, +    private val pendingSelectionCallbackRepo: PendingSelectionCallbackRepository,  ) : DefaultLifecycleObserver {      // This is guaranteed by Hilt, since only a ComponentActivity is injectable.      private val activity: ComponentActivity = hostActivity as ComponentActivity      private val viewModel by activity.viewModels<ChooserViewModel>() +    // TODO: provide the following through an init object passed into [setInitialize]      private lateinit var activityInitializer: Runnable - +    /** Invoked when there are updates to ChooserRequest */      var onChooserRequestChanged: Consumer<ChooserRequest> = Consumer {} +    /** Invoked when there are a new change to payload selection */ +    var onPendingSelection: Runnable = Runnable {}      init {          activity.lifecycle.addObserver(this) @@ -130,8 +135,25 @@ constructor(          }          activity.lifecycleScope.launch { +            val hasPendingCallbackFlow = +                pendingSelectionCallbackRepo.pendingTargetIntent +                    .map { it != null } +                    .distinctUntilChanged() +                    .onEach { hasPendingCallback -> +                        if (hasPendingCallback) { +                            onPendingSelection.run() +                        } +                    }              activity.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { -                viewModel.request.collect { onChooserRequestChanged.accept(it) } +                viewModel.request +                    .combine(hasPendingCallbackFlow) { request, hasPendingCallback -> +                        request to hasPendingCallback +                    } +                    // only take ChooserRequest if there are no pending callbacks +                    .filter { !it.second } +                    .map { it.first } +                    .distinctUntilChanged(areEquivalent = { old, new -> old === new }) +                    .collect { onChooserRequestChanged.accept(it) }              }          }      } diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index e8d4fdde..29b5698b 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -347,9 +347,16 @@ public class ChooserListAdapter extends ResolverListAdapter {                  false);      } +    @Override +    public void onDestroy() { +        super.onDestroy(); +        notifyDataSetChanged(); +    } +      @VisibleForTesting      @Override      public void onBindView(View view, TargetInfo info, int position) { +        view.setEnabled(!isDestroyed());          final ViewHolder holder = (ViewHolder) view.getTag();          resetViewHolder(holder); @@ -478,7 +485,17 @@ public class ChooserListAdapter extends ResolverListAdapter {          }      } +    /** +     * Group application targets +     */      public void updateAlphabeticalList() { +        updateAlphabeticalList(() -> {}); +    } + +    /** +     * Group application targets +     */ +    public void updateAlphabeticalList(Runnable onCompleted) {          final DisplayResolveInfoAzInfoComparator                  comparator = new DisplayResolveInfoAzInfoComparator(mContext);          final List<DisplayResolveInfo> allTargets = new ArrayList<>(); @@ -523,6 +540,7 @@ public class ChooserListAdapter extends ResolverListAdapter {                  mSortedList.clear();                  mSortedList.addAll(newList);                  notifyDataSetChanged(); +                onCompleted.run();              }              private void loadMissingLabels(List<DisplayResolveInfo> targets) { diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index 80d07d2c..9843cf8d 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -448,6 +448,9 @@ public class ResolverListAdapter extends BaseAdapter {          // Send an "incomplete" list-ready while the async task is running.          postListReadyRunnable(doPostProcessing, /* rebuildCompleted */ false);          mBgExecutor.execute(() -> { +            if (isDestroyed()) { +                return; +            }              List<ResolvedComponentInfo> sortedComponents = null;              //TODO: the try-catch logic here is to formally match the AsyncTask's behavior.              // Empirically, we don't need it as in the case on an exception, the app will crash and @@ -785,6 +788,10 @@ public class ResolverListAdapter extends BaseAdapter {          mRequestedLabels.clear();      } +    public final boolean isDestroyed() { +        return mDestroyed.get(); +    } +      private static ColorMatrixColorFilter getSuspendedColorMatrix() {          if (sSuspendedMatrixColorFilter == null) { diff --git a/java/src/com/android/intentresolver/ResolverViewPager.java b/java/src/com/android/intentresolver/ResolverViewPager.java index 0496579d..891ace87 100644 --- a/java/src/com/android/intentresolver/ResolverViewPager.java +++ b/java/src/com/android/intentresolver/ResolverViewPager.java @@ -75,6 +75,12 @@ public class ResolverViewPager extends ViewPager {      @Override      public boolean onInterceptTouchEvent(MotionEvent ev) { -        return !isLayoutRtl() && mSwipingEnabled && super.onInterceptTouchEvent(ev); +        return !isEnabled() +                || (!isLayoutRtl() && mSwipingEnabled && super.onInterceptTouchEvent(ev)); +    } + +    @Override +    public boolean onTouchEvent(MotionEvent ev) { +        return isEnabled() && super.onTouchEvent(ev);      }  } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt index 04416a3d..c202eabf 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt @@ -34,7 +34,7 @@ constructor(          repository.pendingTargetIntent.collectLatest { targetIntent ->              targetIntent ?: return@collectLatest              selectionCallback.onSelectionChanged(targetIntent)?.let { update -> -                chooserRequestInteractor.applyUpdate(update) +                chooserRequestInteractor.applyUpdate(targetIntent, update)              }              repository.pendingTargetIntent.compareAndSet(targetIntent, null)          } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt index 941dfca1..dd16f0c1 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt @@ -34,9 +34,10 @@ constructor(      private val repository: ChooserRequestRepository,      @CustomAction private val pendingIntentSender: PendingIntentSender,  ) { -    fun applyUpdate(update: ShareouselUpdate) { +    fun applyUpdate(targetIntent: Intent, update: ShareouselUpdate) {          repository.chooserRequest.update { current ->              current.copy( +                targetIntent = targetIntent,                  callerChooserTargets =                      update.callerTargets.getOrDefault(current.callerChooserTargets),                  modifyShareAction = diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt index 429e34e9..d99d69ab 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt @@ -31,7 +31,7 @@ constructor(       * sharing application, so that it can react to the new intent.       */      fun updateTargetIntent(targetIntent: Intent) { -        chooserRequestInteractor.setTargetIntent(targetIntent)          repository.pendingTargetIntent.value = targetIntent +        chooserRequestInteractor.setTargetIntent(targetIntent)      }  } diff --git a/java/src/com/android/intentresolver/profiles/MultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/profiles/MultiProfilePagerAdapter.java index 48de37de..11a6caca 100644 --- a/java/src/com/android/intentresolver/profiles/MultiProfilePagerAdapter.java +++ b/java/src/com/android/intentresolver/profiles/MultiProfilePagerAdapter.java @@ -302,15 +302,7 @@ public class MultiProfilePagerAdapter<          viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {              @Override              public void onPageSelected(int position) { -                mCurrentPage = position; -                if (!mLoadedPages.contains(position)) { -                    rebuildActiveTab(true); -                    mLoadedPages.add(position); -                } -                if (mOnProfileSelectedListener != null) { -                    mOnProfileSelectedListener.onProfilePageSelected( -                            getProfileForPageNumber(position), position); -                } +                MultiProfilePagerAdapter.this.onPageSelected(position);              }              @Override @@ -325,6 +317,18 @@ public class MultiProfilePagerAdapter<          mLoadedPages.add(mCurrentPage);      } +    private void onPageSelected(int position) { +        mCurrentPage = position; +        if (!mLoadedPages.contains(position)) { +            rebuildActiveTab(true); +            mLoadedPages.add(position); +        } +        if (mOnProfileSelectedListener != null) { +            mOnProfileSelectedListener.onProfilePageSelected( +                    getProfileForPageNumber(position), position); +        } +    } +      public void clearInactiveProfileCache() {          forEachInactivePage(pageNumber -> mLoadedPages.remove(pageNumber));      } @@ -351,6 +355,13 @@ public class MultiProfilePagerAdapter<          return mCurrentPage;      } +    /** +     * Set active adapter page. A support method for the poayload reselection logic. +     */ +    public void setCurrentPage(int page) { +        onPageSelected(page); +    } +      public final @ProfileType int getActiveProfile() {          return getProfileForPageNumber(getCurrentPage());      }  |