diff options
Diffstat (limited to 'java')
39 files changed, 139 insertions, 933 deletions
diff --git a/java/res/values-b+sr+Latn/strings.xml b/java/res/values-b+sr+Latn/strings.xml index c6f3e894..8867bb06 100644 --- a/java/res/values-b+sr+Latn/strings.xml +++ b/java/res/values-b+sr+Latn/strings.xml @@ -58,12 +58,12 @@ <string name="sharing_text" msgid="8137537443603304062">"Deli se tekst"</string> <string name="sharing_link" msgid="2307694372813942916">"Deli se link"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Deli se slika}one{Deli se # slika}few{Dele se # slike}other{Deli se # slika}}"</string> - <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Deli se video}one{Deli se # video}few{Dele se # video snimka}other{Deli se # video snimaka}}"</string> + <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Deli se video}one{Deli se # video}few{Dele se # video snimka}other{Deli se # videa}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Deli se # fajl}one{Deli se # fajl}few{Dele se # fajla}other{Deli se # fajlova}}"</string> <string name="sharing_images_with_text" msgid="9005717434461730242">"{count,plural, =1{Deli se slika sa tekstom}one{Deli se # slika sa tekstom}few{Dele se # slike sa tekstom}other{Deli se # slika sa tekstom}}"</string> <string name="sharing_images_with_link" msgid="8907893266387877733">"{count,plural, =1{Deli se slika sa linkom}one{Deli se # slika sa linkom}few{Dele se # slike sa linkom}other{Deli se # slika sa linkom}}"</string> - <string name="sharing_videos_with_text" msgid="4169898442482118146">"{count,plural, =1{Deli se video sa tekstom}one{Deli se # video sa tekstom}few{Dele se # video snimka sa tekstom}other{Deli se # video snimaka sa tekstom}}"</string> - <string name="sharing_videos_with_link" msgid="6383290441403042321">"{count,plural, =1{Deli se video sa linkom}one{Deli se # video sa linkom}few{Dele se # video snimka sa linkom}other{Deli se # video snimaka sa linkom}}"</string> + <string name="sharing_videos_with_text" msgid="4169898442482118146">"{count,plural, =1{Deli se video sa tekstom}one{Deli se # video sa tekstom}few{Dele se # video snimka sa tekstom}other{Deli se # videa sa tekstom}}"</string> + <string name="sharing_videos_with_link" msgid="6383290441403042321">"{count,plural, =1{Deli se video sa linkom}one{Deli se # video sa linkom}few{Dele se # video snimka sa linkom}other{Deli se # videa sa linkom}}"</string> <string name="sharing_files_with_text" msgid="7331187260405018080">"{count,plural, =1{Deli se fajl sa tekstom}one{Deli se # fajl sa tekstom}few{Dele se # fajla sa tekstom}other{Deli se # fajlova sa tekstom}}"</string> <string name="sharing_files_with_link" msgid="6052797122358827239">"{count,plural, =1{Deli se fajl sa linkom}one{Deli se # fajl sa linkom}few{Dele se # fajla sa linkom}other{Deli se # fajlova sa linkom}}"</string> <string name="sharing_images_only" msgid="7762589767189955438">"{count,plural, =1{Samo slika}one{Samo slike}few{Samo slike}other{Samo slike}}"</string> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 801e27da..ba51a67e 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -32,7 +32,7 @@ <string name="whichEditApplicationLabel" msgid="5992662938338600364">"ویرایش"</string> <string name="whichSendApplication" msgid="59510564281035884">"همرسانی"</string> <string name="whichSendApplicationNamed" msgid="495577664218765855">"همرسانی با <xliff:g id="APP">%1$s</xliff:g>"</string> - <string name="whichSendApplicationLabel" msgid="2391198069286568035">"اشتراکگذاری"</string> + <string name="whichSendApplicationLabel" msgid="2391198069286568035">"همرسانی"</string> <string name="whichSendToApplication" msgid="2724450540348806267">"ارسال با استفاده از"</string> <string name="whichSendToApplicationNamed" msgid="1996548940365954543">"ارسال بااستفاده از <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="whichSendToApplicationLabel" msgid="6909037198280591110">"ارسال"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 13cc927d..2ff9cbe9 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -58,12 +58,12 @@ <string name="sharing_text" msgid="8137537443603304062">"Дели се текст"</string> <string name="sharing_link" msgid="2307694372813942916">"Дели се линк"</string> <string name="sharing_images" msgid="5251443722186962006">"{count,plural, =1{Дели се слика}one{Дели се # слика}few{Деле се # слике}other{Дели се # слика}}"</string> - <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Дели се видео}one{Дели се # видео}few{Деле се # видео снимка}other{Дели се # видео снимака}}"</string> + <string name="sharing_videos" msgid="3583423190182877434">"{count,plural, =1{Дели се видео}one{Дели се # видео}few{Деле се # видео снимка}other{Дели се # видеа}}"</string> <string name="sharing_files" msgid="1275646542246028823">"{count,plural, =1{Дели се # фајл}one{Дели се # фајл}few{Деле се # фајла}other{Дели се # фајлова}}"</string> <string name="sharing_images_with_text" msgid="9005717434461730242">"{count,plural, =1{Дели се слика са текстом}one{Дели се # слика са текстом}few{Деле се # слике са текстом}other{Дели се # слика са текстом}}"</string> <string name="sharing_images_with_link" msgid="8907893266387877733">"{count,plural, =1{Дели се слика са линком}one{Дели се # слика са линком}few{Деле се # слике са линком}other{Дели се # слика са линком}}"</string> - <string name="sharing_videos_with_text" msgid="4169898442482118146">"{count,plural, =1{Дели се видео са текстом}one{Дели се # видео са текстом}few{Деле се # видео снимка са текстом}other{Дели се # видео снимака са текстом}}"</string> - <string name="sharing_videos_with_link" msgid="6383290441403042321">"{count,plural, =1{Дели се видео са линком}one{Дели се # видео са линком}few{Деле се # видео снимка са линком}other{Дели се # видео снимака са линком}}"</string> + <string name="sharing_videos_with_text" msgid="4169898442482118146">"{count,plural, =1{Дели се видео са текстом}one{Дели се # видео са текстом}few{Деле се # видео снимка са текстом}other{Дели се # видеа са текстом}}"</string> + <string name="sharing_videos_with_link" msgid="6383290441403042321">"{count,plural, =1{Дели се видео са линком}one{Дели се # видео са линком}few{Деле се # видео снимка са линком}other{Дели се # видеа са линком}}"</string> <string name="sharing_files_with_text" msgid="7331187260405018080">"{count,plural, =1{Дели се фајл са текстом}one{Дели се # фајл са текстом}few{Деле се # фајла са текстом}other{Дели се # фајлова са текстом}}"</string> <string name="sharing_files_with_link" msgid="6052797122358827239">"{count,plural, =1{Дели се фајл са линком}one{Дели се # фајл са линком}few{Деле се # фајла са линком}other{Дели се # фајлова са линком}}"</string> <string name="sharing_images_only" msgid="7762589767189955438">"{count,plural, =1{Само слика}one{Само слике}few{Само слике}other{Само слике}}"</string> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 4b5367c0..0c772573 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -303,4 +303,8 @@ <string name="exclude_link">Exclude link</string> <!-- Title for a button. Adds back a (previously excluded) web link into the shared content. --> <string name="include_link">Include link</string> + + <!-- Accesssibility content description for a sharesheet target that has been pinned to the + front of the list by the user. [CHAR LIMIT=NONE] --> + <string name="pinned">Pinned</string> </resources> diff --git a/java/src/com/android/intentresolver/ApplicationComponentOwner.kt b/java/src/com/android/intentresolver/ApplicationComponentOwner.kt deleted file mode 100644 index fb39814c..00000000 --- a/java/src/com/android/intentresolver/ApplicationComponentOwner.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.android.intentresolver - -import com.android.intentresolver.dagger.ApplicationComponent - -/** - * Interface that should be implemented by the [Application][android.app.Application] object as the - * owner of the [ApplicationComponent]. - */ -interface ApplicationComponentOwner { - /** - * Invokes the given [action] when the [ApplicationComponent] has been created. If it has - * already been created, then it invokes [action] immediately. - */ - fun doWhenApplicationComponentReady(action: (ApplicationComponent) -> Unit) -} diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 8edbba08..b27f054e 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -27,7 +27,6 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET; import android.annotation.IntDef; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -66,13 +65,9 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowInsets; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.view.animation.LinearInterpolator; import android.widget.TextView; import androidx.annotation.MainThread; -import androidx.lifecycle.HasDefaultViewModelProviderFactory; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -88,8 +83,6 @@ import com.android.intentresolver.contentpreview.BasePreviewViewModel; import com.android.intentresolver.contentpreview.ChooserContentPreviewUi; import com.android.intentresolver.contentpreview.HeadlineGeneratorImpl; import com.android.intentresolver.contentpreview.PreviewViewModel; -import com.android.intentresolver.dagger.InjectedViewModelFactory; -import com.android.intentresolver.dagger.ViewModelComponent; import com.android.intentresolver.flags.FeatureFlagRepository; import com.android.intentresolver.flags.FeatureFlagRepositoryFactory; import com.android.intentresolver.grid.ChooserGridAdapter; @@ -102,14 +95,11 @@ 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.ui.ChooserViewModel; import com.android.intentresolver.widget.ImagePreviewView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -125,15 +115,13 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; -import javax.inject.Inject; - /** * The Chooser Activity handles intent resolution specifically for sharing intents - * for example, as generated by {@see android.content.Intent#createChooser(Intent, CharSequence)}. * */ public class ChooserActivity extends ResolverActivity implements - ResolverListAdapter.ResolverListCommunicator, HasDefaultViewModelProviderFactory { + ResolverListAdapter.ResolverListCommunicator { private static final String TAG = "ChooserActivity"; /** @@ -173,11 +161,6 @@ public class ChooserActivity extends ResolverActivity implements private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1; private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2; - private ViewModelProvider.Factory mViewModelFactory; - private final ViewModelComponent.Builder mViewModelComponentBuilder; - - private ChooserViewModel mViewModel; - @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = { TARGET_TYPE_DEFAULT, TARGET_TYPE_CHOOSER_TARGET, @@ -238,34 +221,24 @@ public class ChooserActivity extends ResolverActivity implements private final SparseArray<ProfileRecord> mProfileRecords = new SparseArray<>(); private boolean mExcludeSharedText = false; + /** + * When we intend to finish the activity with a shared element transition, we can't immediately + * finish() when the transition is invoked, as the receiving end may not be able to start the + * animation and the UI breaks if this takes too long. Instead we defer finishing until onStop + * in order to wait for the transition to begin. + */ + private boolean mFinishWhenStopped = false; - @Inject - public ChooserActivity(ViewModelComponent.Builder builder) { - mViewModelComponentBuilder = builder; - } - - @NotNull - @Override - public final ViewModelProvider.Factory getDefaultViewModelProviderFactory() { - if (mViewModelFactory == null) { - mViewModelFactory = new InjectedViewModelFactory(mViewModelComponentBuilder, - getDefaultViewModelCreationExtras(), - getReferrer()); - } - return mViewModelFactory; - } + public ChooserActivity() {} @Override protected void onCreate(Bundle savedInstanceState) { - Log.d(TAG, "onCreate"); Tracer.INSTANCE.markLaunched(); final long intentReceivedTime = System.currentTimeMillis(); mLatencyTracker.onActionStart(ACTION_LOAD_SHARE_SHEET); getEventLog().logSharesheetTriggered(); - mViewModel = new ViewModelProvider(this).get(ChooserViewModel.class); - mFeatureFlagRepository = createFeatureFlagRepository(); mIntegratedDeviceComponents = getIntegratedDeviceComponents(); @@ -282,9 +255,7 @@ public class ChooserActivity extends ResolverActivity implements return; } - // Note: Uses parent ViewModelProvider.Factory because RefinementManager is not injectable - mRefinementManager = new ViewModelProvider(this, super.getDefaultViewModelProviderFactory()) - .get(ChooserRefinementManager.class); + mRefinementManager = new ViewModelProvider(this).get(ChooserRefinementManager.class); mRefinementManager.getRefinementCompletion().observe(this, completion -> { if (completion.consume()) { @@ -306,7 +277,7 @@ public class ChooserActivity extends ResolverActivity implements BasePreviewViewModel previewViewModel = new ViewModelProvider(this, createPreviewViewModelFactory()) - .get(PreviewViewModel.class); + .get(BasePreviewViewModel.class); mChooserContentPreviewUi = new ChooserContentPreviewUi( getLifecycle(), previewViewModel.createOrReuseProvider(mChooserRequest), @@ -645,8 +616,7 @@ public class ChooserActivity extends ResolverActivity implements protected void onResume() { super.onResume(); Log.d(TAG, "onResume: " + getComponentName().flattenToShortString()); - maybeCancelFinishAnimation(); - + mFinishWhenStopped = false; mRefinementManager.onActivityResume(); } @@ -747,7 +717,8 @@ public class ChooserActivity extends ResolverActivity implements super.onStop(); mRefinementManager.onActivityStop(isChangingConfigurations()); - if (maybeCancelFinishAnimation()) { + if (mFinishWhenStopped) { + mFinishWhenStopped = false; finish(); } } @@ -1363,7 +1334,10 @@ public class ChooserActivity extends ResolverActivity implements ChooserActivity.this, sharedElement, sharedElementName); safelyStartActivityAsUser( targetInfo, getPersonalProfileUserHandle(), options.toBundle()); - startFinishAnimation(); + // Can't finish right away because the shared element transition may not + // be ready to start. + mFinishWhenStopped = true; + } }, (status) -> { @@ -1748,25 +1722,6 @@ public class ChooserActivity extends ResolverActivity implements contentPreviewContainer.setVisibility(View.GONE); } - private void startFinishAnimation() { - View rootView = findRootView(); - if (rootView != null) { - rootView.startAnimation(new FinishAnimation(this, rootView)); - } - } - - private boolean maybeCancelFinishAnimation() { - View rootView = findRootView(); - Animation animation = (rootView == null) ? null : rootView.getAnimation(); - if (animation instanceof FinishAnimation) { - boolean hasEnded = animation.hasEnded(); - animation.cancel(); - rootView.clearAnimation(); - return !hasEnded; - } - return false; - } - private View findRootView() { if (mContentView == null) { mContentView = findViewById(android.R.id.content); @@ -1847,71 +1802,6 @@ public class ChooserActivity extends ResolverActivity implements } } - /** - * Used in combination with the scene transition when launching the image editor - */ - private static class FinishAnimation extends AlphaAnimation implements - Animation.AnimationListener { - @Nullable - private Activity mActivity; - @Nullable - private View mRootView; - private final float mFromAlpha; - - FinishAnimation(@NonNull Activity activity, @NonNull View rootView) { - super(rootView.getAlpha(), 0.0f); - mActivity = activity; - mRootView = rootView; - mFromAlpha = rootView.getAlpha(); - setInterpolator(new LinearInterpolator()); - long duration = activity.getWindow().getTransitionBackgroundFadeDuration(); - setDuration(duration); - // The scene transition animation looks better when it's not overlapped with this - // fade-out animation thus the delay. - // It is most likely that the image editor will cause this activity to stop and this - // animation will be cancelled in the background without running (i.e. we'll animate - // only when this activity remains partially visible after the image editor launch). - setStartOffset(duration); - super.setAnimationListener(this); - } - - @Override - public void setAnimationListener(AnimationListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void cancel() { - if (mRootView != null) { - mRootView.setAlpha(mFromAlpha); - } - cleanup(); - super.cancel(); - } - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - Activity activity = mActivity; - cleanup(); - if (activity != null) { - activity.finish(); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - private void cleanup() { - mActivity = null; - mRootView = null; - } - } - @Override protected void maybeLogProfileChange() { getEventLog().logSharesheetProfileChanged(); diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index e6d6dbf4..d1101f1e 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -296,11 +296,23 @@ public class ChooserListAdapter extends ResolverListAdapter { CharSequence extendedInfo = info.getExtendedInfo(); String contentDescription = String.join(" ", info.getDisplayLabel(), extendedInfo != null ? extendedInfo : "", appName); + if (info.isPinned()) { + contentDescription = String.join( + ". ", + contentDescription, + mContext.getResources().getString(R.string.pinned)); + } holder.updateContentDescription(contentDescription); if (!info.hasDisplayIcon()) { loadDirectShareIcon((SelectableTargetInfo) info); } } else if (info.isDisplayResolveInfo()) { + if (info.isPinned()) { + holder.updateContentDescription(String.join( + ". ", + info.getDisplayLabel(), + mContext.getResources().getString(R.string.pinned))); + } DisplayResolveInfo dri = (DisplayResolveInfo) info; if (!dri.hasDisplayIcon()) { loadIcon(dri); @@ -384,18 +396,20 @@ public class ChooserListAdapter extends ResolverListAdapter { .stream() .collect(Collectors.groupingBy(target -> target.getResolvedComponentName().getPackageName() - + "#" + target.getDisplayLabel() - + '#' + target.getResolveInfo().userHandle.getIdentifier() + + "#" + target.getDisplayLabel() + + '#' + target.getResolveInfo().userHandle.getIdentifier() )) .values() .stream() .map(appTargets -> (appTargets.size() == 1) - ? appTargets.get(0) - : MultiDisplayResolveInfo.newMultiDisplayResolveInfo(appTargets)) + ? appTargets.get(0) + : MultiDisplayResolveInfo.newMultiDisplayResolveInfo( + appTargets)) .sorted(new ChooserActivity.AzInfoComparator(mContext)) .collect(Collectors.toList()); } + @Override protected void onPostExecute(List<DisplayResolveInfo> newList) { mSortedList = newList; @@ -645,8 +659,8 @@ public class ChooserListAdapter extends ResolverListAdapter { */ @Override AsyncTask<List<ResolvedComponentInfo>, - Void, - List<ResolvedComponentInfo>> createSortingTask(boolean doPostProcessing) { + Void, + List<ResolvedComponentInfo>> createSortingTask(boolean doPostProcessing) { return new AsyncTask<List<ResolvedComponentInfo>, Void, List<ResolvedComponentInfo>>() { @@ -658,6 +672,7 @@ public class ChooserListAdapter extends ResolverListAdapter { Trace.endSection(); return params[0]; } + @Override protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) { processSortedList(sortedComponents, doPostProcessing); diff --git a/java/src/com/android/intentresolver/IntentForwarderActivity.java b/java/src/com/android/intentresolver/IntentForwarderActivity.java index d69a6c71..5e8945f1 100644 --- a/java/src/com/android/intentresolver/IntentForwarderActivity.java +++ b/java/src/com/android/intentresolver/IntentForwarderActivity.java @@ -57,8 +57,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.inject.Inject; - /** * This is used in conjunction with * {@link DevicePolicyManager#addCrossProfileIntentFilter} to enable intents to @@ -86,11 +84,6 @@ public class IntentForwarderActivity extends Activity { private MetricsLogger mMetricsLogger; protected ExecutorService mExecutorService; - @Inject - public IntentForwarderActivity() { - super(); - } - @Override protected void onDestroy() { super.onDestroy(); diff --git a/java/src/com/android/intentresolver/IntentResolverApplication.kt b/java/src/com/android/intentresolver/IntentResolverApplication.kt deleted file mode 100644 index 61df7fff..00000000 --- a/java/src/com/android/intentresolver/IntentResolverApplication.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.android.intentresolver - -import android.app.Application -import com.android.intentresolver.dagger.ApplicationComponent -import com.android.intentresolver.dagger.DaggerApplicationComponent - -/** [Application] that maintains the [ApplicationComponent]. */ -open class IntentResolverApplication : Application(), ApplicationComponentOwner { - - private lateinit var applicationComponent: ApplicationComponent - - private val pendingDaggerActions = mutableSetOf<(ApplicationComponent) -> Unit>() - - open fun createApplicationComponentBuilder() = DaggerApplicationComponent.builder() - - override fun onCreate() { - super.onCreate() - applicationComponent = createApplicationComponentBuilder().application(this).build() - pendingDaggerActions.forEach { it.invoke(applicationComponent) } - pendingDaggerActions.clear() - } - - override fun doWhenApplicationComponentReady(action: (ApplicationComponent) -> Unit) { - if (this::applicationComponent.isInitialized) { - action.invoke(applicationComponent) - } else { - pendingDaggerActions.add(action) - } - } -} diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index 252d0a41..35c7e897 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -127,8 +127,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Supplier; -import javax.inject.Inject; - /** * This is a copy of ResolverActivity to support IntentResolver's ChooserActivity. This code is * *not* the resolver that is actually triggered by the system right now (you want @@ -139,7 +137,6 @@ import javax.inject.Inject; public class ResolverActivity extends FragmentActivity implements ResolverListAdapter.ResolverListCommunicator { - @Inject public ResolverActivity() { mIsIntentPicker = getClass().equals(ResolverActivity.class); } diff --git a/java/src/com/android/intentresolver/dagger/ActivityBinderModule.kt b/java/src/com/android/intentresolver/dagger/ActivityBinderModule.kt deleted file mode 100644 index 7c997ef7..00000000 --- a/java/src/com/android/intentresolver/dagger/ActivityBinderModule.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.android.intentresolver.dagger - -import android.app.Activity -import com.android.intentresolver.ChooserActivity -import com.android.intentresolver.IntentForwarderActivity -import com.android.intentresolver.ResolverActivity -import dagger.Binds -import dagger.Module -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -/** Injection instructions for injectable [Activities][Activity]. */ -@Module -interface ActivityBinderModule { - - @Binds - @IntoMap - @ClassKey(ChooserActivity::class) - @ActivityScope - fun bindChooserActivity(activity: ChooserActivity): Activity - - @Binds - @IntoMap - @ClassKey(ResolverActivity::class) - @ActivityScope - fun bindResolverActivity(activity: ResolverActivity): Activity - - @Binds - @IntoMap - @ClassKey(IntentForwarderActivity::class) - @ActivityScope - fun bindIntentForwarderActivity(activity: IntentForwarderActivity): Activity -} diff --git a/java/src/com/android/intentresolver/dagger/ActivityComponent.kt b/java/src/com/android/intentresolver/dagger/ActivityComponent.kt deleted file mode 100644 index bf5ff761..00000000 --- a/java/src/com/android/intentresolver/dagger/ActivityComponent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.intentresolver.dagger - -import android.app.Activity -import dagger.Subcomponent -import javax.inject.Provider -import javax.inject.Scope - -@MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Scope annotation class ActivityScope - -/** Subcomponent for injections across the life of an Activity. */ -@ActivityScope -@Subcomponent(modules = [ActivityModule::class, ActivityBinderModule::class]) -interface ActivityComponent { - - @Subcomponent.Factory - interface Factory { - fun create(): ActivityComponent - } - - fun activities(): Map<Class<*>, @JvmSuppressWildcards Provider<Activity>> -} diff --git a/java/src/com/android/intentresolver/dagger/ActivityModule.kt b/java/src/com/android/intentresolver/dagger/ActivityModule.kt deleted file mode 100644 index f6a2229d..00000000 --- a/java/src/com/android/intentresolver/dagger/ActivityModule.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.android.intentresolver.dagger - -import dagger.Module - -/** Bindings provided to [@ActivityScope][ActivityScope]. */ -@Module interface ActivityModule diff --git a/java/src/com/android/intentresolver/dagger/ApplicationComponent.kt b/java/src/com/android/intentresolver/dagger/ApplicationComponent.kt deleted file mode 100644 index 9fc57712..00000000 --- a/java/src/com/android/intentresolver/dagger/ApplicationComponent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.intentresolver.dagger - -import android.app.Application -import dagger.BindsInstance -import dagger.Component -import javax.inject.Singleton - -/** Top level component for injections across the life of the process. */ -@Singleton -@Component(modules = [ApplicationModule::class]) -interface ApplicationComponent { - - @Component.Builder - interface Builder { - @BindsInstance fun application(application: Application): Builder - - fun build(): ApplicationComponent - } - - fun inject(appComponentFactory: InjectedAppComponentFactory) -} diff --git a/java/src/com/android/intentresolver/dagger/ApplicationModule.kt b/java/src/com/android/intentresolver/dagger/ApplicationModule.kt deleted file mode 100644 index 4986d7e1..00000000 --- a/java/src/com/android/intentresolver/dagger/ApplicationModule.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.android.intentresolver.dagger - -import android.app.Application -import android.content.Context -import com.android.intentresolver.dagger.qualifiers.App -import dagger.Module -import dagger.Provides -import javax.inject.Singleton - -/** - * Bindings provided to [ApplicationComponent] and children. - * - * These are all @Singleton scope, one for the duration of the process. - */ -@Module( - subcomponents = [ActivityComponent::class, ViewModelComponent::class], - includes = [ReceiverBinderModule::class, CoroutinesModule::class], -) -interface ApplicationModule { - - companion object { - - @JvmStatic - @Provides - @Singleton - @App - fun applicationContext(app: Application): Context = app.applicationContext - } -} diff --git a/java/src/com/android/intentresolver/dagger/CoroutinesModule.kt b/java/src/com/android/intentresolver/dagger/CoroutinesModule.kt deleted file mode 100644 index 5fda2c30..00000000 --- a/java/src/com/android/intentresolver/dagger/CoroutinesModule.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import com.android.intentresolver.dagger.qualifiers.Background -import com.android.intentresolver.dagger.qualifiers.Main -import dagger.Module -import dagger.Provides -import javax.inject.Singleton -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob - -@Module -interface CoroutinesModule { - companion object { - @JvmStatic - @Provides - @Singleton - @Main - fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate - - @JvmStatic - @Provides - @Singleton - @Main - fun mainCoroutineScope(@Main mainDispatcher: CoroutineDispatcher) = - CoroutineScope(SupervisorJob() + mainDispatcher) - - @JvmStatic - @Provides - @Singleton - @Background - fun backgroundDispatcher(): CoroutineDispatcher = Dispatchers.IO - } -} diff --git a/java/src/com/android/intentresolver/dagger/InjectedAppComponentFactory.kt b/java/src/com/android/intentresolver/dagger/InjectedAppComponentFactory.kt deleted file mode 100644 index db209ef0..00000000 --- a/java/src/com/android/intentresolver/dagger/InjectedAppComponentFactory.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import android.app.Activity -import android.app.Application -import android.content.BroadcastReceiver -import android.content.Intent -import android.util.Log -import androidx.core.app.AppComponentFactory -import com.android.intentresolver.ApplicationComponentOwner -import javax.inject.Inject -import javax.inject.Provider - -/** Provides instances of application components, delegates construction to Dagger. */ -class InjectedAppComponentFactory : AppComponentFactory() { - - @set:Inject lateinit var activityComponentBuilder: ActivityComponent.Factory - - @set:Inject - lateinit var receivers: Map<Class<*>, @JvmSuppressWildcards Provider<BroadcastReceiver>> - - override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application { - val app = super.instantiateApplicationCompat(cl, className) - if (app !is ApplicationComponentOwner) { - throw RuntimeException("App must be ApplicationComponentOwner") - } - app.doWhenApplicationComponentReady { it.inject(this) } - return app - } - - override fun instantiateActivityCompat( - cl: ClassLoader, - className: String, - intent: Intent?, - ): Activity { - return runCatching { - val activities = activityComponentBuilder.create().activities() - instantiate(className, activities) - } - .onFailure { - if (it is UninitializedPropertyAccessException) { - // This should never happen but if it did it would cause errors that could - // be very difficult to identify, so we log it out of an abundance of - // caution. - Log.e(TAG, "Tried to instantiate $className before AppComponent", it) - } - } - .getOrNull() - ?: super.instantiateActivityCompat(cl, className, intent) - } - - override fun instantiateReceiverCompat( - cl: ClassLoader, - className: String, - intent: Intent?, - ): BroadcastReceiver { - return instantiate(className, receivers) - ?: super.instantiateReceiverCompat(cl, className, intent) - } - - private fun <T> instantiate(className: String, providers: Map<Class<*>, Provider<T>>): T? { - return runCatching { providers[Class.forName(className)]?.get() } - .onFailure { - if (it is UninitializedPropertyAccessException) { - // This should never happen but if it did it would cause errors that could - // be very difficult to identify, so we log it out of an abundance of - // caution. - Log.e(TAG, "Tried to instantiate $className before AppComponent", it) - } - } - .getOrNull() - } - - companion object { - private const val TAG = "AppComponentFactory" - } -} diff --git a/java/src/com/android/intentresolver/dagger/InjectedViewModelFactory.kt b/java/src/com/android/intentresolver/dagger/InjectedViewModelFactory.kt deleted file mode 100644 index f0906d3e..00000000 --- a/java/src/com/android/intentresolver/dagger/InjectedViewModelFactory.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import android.net.Uri -import android.os.Bundle -import androidx.lifecycle.DEFAULT_ARGS_KEY -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import java.io.Closeable -import javax.inject.Provider -import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.isActive - -/** Instantiates new ViewModel instances using Dagger. */ -class InjectedViewModelFactory( - private val viewModelComponentBuilder: ViewModelComponent.Builder, - creationExtras: CreationExtras, - private val referrer: Uri, -) : ViewModelProvider.Factory { - - private val defaultArgs = creationExtras[DEFAULT_ARGS_KEY] ?: Bundle() - - private fun viewModelScope(viewModelClass: Class<*>) = - CloseableCoroutineScope( - SupervisorJob() + CoroutineName(viewModelClass.simpleName) + Dispatchers.Main.immediate - ) - - private fun <T> newViewModel( - providerMap: Map<Class<*>, Provider<ViewModel>>, - modelClass: Class<T> - ): T { - val provider = - providerMap[modelClass] - ?: error( - "Unable to create an instance of $modelClass. " + - "Does the class have a binding in ViewModelComponent?" - ) - return modelClass.cast(provider.get()) - } - - override fun <T : ViewModel> create(modelClass: Class<T>): T { - val viewModelScope = viewModelScope(modelClass) - val viewModelComponent = - viewModelComponentBuilder - .coroutineScope(viewModelScope) - .intentExtras(defaultArgs) - .referrer(referrer) - .build() - val viewModel = newViewModel(viewModelComponent.viewModels(), modelClass) - viewModel.addCloseable(viewModelScope) - return viewModel - } -} - -internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope { - override val coroutineContext: CoroutineContext = context - - override fun close() { - if (isActive) { - coroutineContext.cancel() - } - } -} diff --git a/java/src/com/android/intentresolver/dagger/ReceiverBinderModule.kt b/java/src/com/android/intentresolver/dagger/ReceiverBinderModule.kt deleted file mode 100644 index 32ce2f45..00000000 --- a/java/src/com/android/intentresolver/dagger/ReceiverBinderModule.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.android.intentresolver.dagger - -import android.content.BroadcastReceiver -import dagger.Module -import dagger.multibindings.Multibinds - -/** Injection instructions for injectable [BroadcastReceivers][BroadcastReceiver] */ -@Module -interface ReceiverBinderModule { - - @Multibinds fun bindReceivers(): Map<Class<*>, @JvmSuppressWildcards BroadcastReceiver> -} diff --git a/java/src/com/android/intentresolver/dagger/ViewModelBinderModule.kt b/java/src/com/android/intentresolver/dagger/ViewModelBinderModule.kt deleted file mode 100644 index 91ba039c..00000000 --- a/java/src/com/android/intentresolver/dagger/ViewModelBinderModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import androidx.lifecycle.ViewModel -import com.android.intentresolver.ui.ChooserViewModel -import dagger.Binds -import dagger.Module -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -/** Defines a map of injectable ViewModel classes. */ -@Module -interface ViewModelBinderModule { - @Binds - @IntoMap - @ClassKey(ChooserViewModel::class) - @ViewModelScope - fun chooserViewModel(viewModel: ChooserViewModel): ViewModel -} diff --git a/java/src/com/android/intentresolver/dagger/ViewModelComponent.kt b/java/src/com/android/intentresolver/dagger/ViewModelComponent.kt deleted file mode 100644 index 3e2e2681..00000000 --- a/java/src/com/android/intentresolver/dagger/ViewModelComponent.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import android.net.Uri -import android.os.Bundle -import com.android.intentresolver.dagger.qualifiers.Referrer -import com.android.intentresolver.dagger.qualifiers.ViewModel -import dagger.BindsInstance -import dagger.Subcomponent -import javax.inject.Provider -import javax.inject.Scope -import kotlin.annotation.AnnotationRetention.RUNTIME -import kotlinx.coroutines.CoroutineScope - -@Scope @Retention(RUNTIME) @MustBeDocumented annotation class ViewModelScope - -/** - * Provides dependencies within [ViewModelScope] within a [ViewModel]. - * - * @see InjectedViewModelFactory - */ -@ViewModelScope -@Subcomponent(modules = [ViewModelModule::class, ViewModelBinderModule::class]) -interface ViewModelComponent { - - /** - * Binds instance values from the creating Activity to make them available for injection within - * [ViewModelScope]. - */ - @Subcomponent.Builder - interface Builder { - @BindsInstance fun intentExtras(@ViewModel intentExtras: Bundle): Builder - - @BindsInstance fun referrer(@Referrer uri: Uri): Builder - - @BindsInstance fun coroutineScope(@ViewModel scope: CoroutineScope): Builder - - fun build(): ViewModelComponent - } - - fun viewModels(): Map<Class<*>, @JvmSuppressWildcards Provider<androidx.lifecycle.ViewModel>> -} diff --git a/java/src/com/android/intentresolver/dagger/ViewModelModule.kt b/java/src/com/android/intentresolver/dagger/ViewModelModule.kt deleted file mode 100644 index 23320311..00000000 --- a/java/src/com/android/intentresolver/dagger/ViewModelModule.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.android.intentresolver.dagger - -import dagger.Module - -/** Provides bindings shared among components within [@ViewModelScope][ViewModelScope]. */ -@Module abstract class ViewModelModule diff --git a/java/src/com/android/intentresolver/dagger/qualifiers/Qualifiers.kt b/java/src/com/android/intentresolver/dagger/qualifiers/Qualifiers.kt deleted file mode 100644 index fa50170e..00000000 --- a/java/src/com/android/intentresolver/dagger/qualifiers/Qualifiers.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger.qualifiers - -import javax.inject.Qualifier - -// Note: 'qualifiers' package avoids name collisions in Dagger code. - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class App - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Activity - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ViewModel - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Main - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Background - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Delegate - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Default - -@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class Referrer diff --git a/java/src/com/android/intentresolver/ui/ChooserViewModel.kt b/java/src/com/android/intentresolver/ui/ChooserViewModel.kt deleted file mode 100644 index 817f0b6c..00000000 --- a/java/src/com/android/intentresolver/ui/ChooserViewModel.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 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.ui - -import android.util.Log -import androidx.lifecycle.ViewModel -import com.android.intentresolver.dagger.qualifiers.ViewModel as ViewModelQualifier -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.awaitCancellation -import kotlinx.coroutines.launch - -const val TAG = "ChooserViewModel" - -/** The primary container for ViewModelScope dependencies. */ -class ChooserViewModel -@Inject -constructor( - @ViewModelQualifier val viewModelScope: CoroutineScope, -) : ViewModel()
\ No newline at end of file diff --git a/java/tests/Android.bp b/java/tests/Android.bp index 3936b38e..29ecd95d 100644 --- a/java/tests/Android.bp +++ b/java/tests/Android.bp @@ -20,6 +20,7 @@ android_test { static_libs: [ "IntentResolver-core", "androidx.test.core", + "androidx.test.rules", "androidx.test.ext.junit", "androidx.test.ext.truth", "androidx.test.espresso.contrib", @@ -34,6 +35,8 @@ android_test { "testables", "truth-prebuilt", ], + min_sdk_version: "current", + target_sdk_version: "current", plugins: ["dagger2-compiler"], test_suites: ["general-tests"], sdk_version: "core_platform", diff --git a/java/tests/AndroidManifest.xml b/java/tests/AndroidManifest.xml index 9f8dd41c..4517a60c 100644 --- a/java/tests/AndroidManifest.xml +++ b/java/tests/AndroidManifest.xml @@ -15,10 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.intentresolver.tests"> - - <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" /> + package="com.android.intentresolver.tests"> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.QUERY_USERS"/> @@ -26,10 +23,7 @@ <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> - <application - android:name="com.android.intentresolver.TestApplication" - tools:replace="android:appComponentFactory" - android:appComponentFactory="com.android.intentresolver.dagger.InjectedAppComponentFactory"> + <application android:name="com.android.intentresolver.TestApplication"> <uses-library android:name="android.test.runner" /> <activity android:name="com.android.intentresolver.ChooserWrapperActivity" /> <activity android:name="com.android.intentresolver.ResolverWrapperActivity" /> diff --git a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt index 7f06e683..2d1ac4e4 100644 --- a/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt +++ b/java/tests/src/com/android/intentresolver/ChooserActionFactoryTest.kt @@ -20,6 +20,7 @@ import android.app.Activity import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context +import android.content.Context.RECEIVER_EXPORTED import android.content.Intent import android.content.IntentFilter import android.content.res.Resources @@ -69,7 +70,7 @@ class ChooserActionFactoryTest { @Before fun setup() { - context.registerReceiver(testReceiver, IntentFilter(testAction)) + context.registerReceiver(testReceiver, IntentFilter(testAction), RECEIVER_EXPORTED) } @After @@ -190,7 +191,8 @@ class ChooserActionFactoryTest { } private fun createFactory(includeModifyShare: Boolean = false): ChooserActionFactory { - val testPendingIntent = PendingIntent.getActivity(context, 0, Intent(testAction), 0) + val testPendingIntent = + PendingIntent.getActivity(context, 0, Intent(testAction), PendingIntent.FLAG_IMMUTABLE) val targetIntent = Intent() val action = ChooserAction.Builder( diff --git a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt index c8cb4b9b..9b5e2d1c 100644 --- a/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt +++ b/java/tests/src/com/android/intentresolver/ChooserListAdapterTest.kt @@ -20,6 +20,7 @@ import android.content.ComponentName import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ShortcutInfo import android.os.UserHandle import android.view.View import android.widget.FrameLayout @@ -33,6 +34,7 @@ import com.android.intentresolver.chooser.TargetInfo import com.android.intentresolver.icons.TargetDataLoader import com.android.intentresolver.logging.EventLog import com.android.internal.R +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -50,6 +52,8 @@ class ChooserListAdapterTest { } private val context = InstrumentationRegistry.getInstrumentation().context private val resolverListController = mock<ResolverListController>() + private val appLabel = "App" + private val targetLabel = "Target" private val mEventLog = mock<EventLog>() private val mTargetDataLoader = mock<TargetDataLoader>() @@ -132,29 +136,77 @@ class ChooserListAdapterTest { verify(mTargetDataLoader, times(1)).loadAppTargetIcon(any(), any(), any()) } - private fun createSelectableTargetInfo(): TargetInfo = - SelectableTargetInfo.newSelectableTargetInfo( - /* sourceInfo = */ DisplayResolveInfo.newDisplayResolveInfo( - Intent(), - ResolverDataProvider.createResolveInfo(2, 0, userHandle), - "label", - "extended info", - Intent(), - /* resolveInfoPresentationGetter= */ null - ), + @Test + fun onBindView_contentDescription() { + val view = createView() + val viewHolder = ResolverListAdapter.ViewHolder(view) + view.tag = viewHolder + val targetInfo = createSelectableTargetInfo() + testSubject.onBindView(view, targetInfo, 0) + + assertThat(view.contentDescription).isEqualTo("$targetLabel $appLabel") + } + + @Test + fun onBindView_contentDescriptionPinned() { + val view = createView() + val viewHolder = ResolverListAdapter.ViewHolder(view) + view.tag = viewHolder + val targetInfo = createSelectableTargetInfo(true) + testSubject.onBindView(view, targetInfo, 0) + + assertThat(view.contentDescription).isEqualTo("$targetLabel $appLabel. Pinned") + } + + @Test + fun onBindView_displayInfoContentDescriptionPinned() { + val view = createView() + val viewHolder = ResolverListAdapter.ViewHolder(view) + view.tag = viewHolder + val targetInfo = createDisplayResolveInfo(isPinned = true) + testSubject.onBindView(view, targetInfo, 0) + + assertThat(view.contentDescription).isEqualTo("$appLabel. Pinned") + } + + private fun createSelectableTargetInfo(isPinned: Boolean = false): TargetInfo { + val shortcutInfo = + createShortcutInfo("id-1", ComponentName("pkg", "Class"), 1).apply { + if (isPinned) { + addFlags(ShortcutInfo.FLAG_PINNED) + } + } + return SelectableTargetInfo.newSelectableTargetInfo( + /* sourceInfo = */ createDisplayResolveInfo(isPinned), /* backupResolveInfo = */ mock(), /* resolvedIntent = */ Intent(), /* chooserTarget = */ createChooserTarget( - "Target", + targetLabel, 0.5f, ComponentName("pkg", "Class"), "id-1" ), /* modifiedScore = */ 1f, - /* shortcutInfo = */ createShortcutInfo("id-1", ComponentName("pkg", "Class"), 1), + shortcutInfo, /* appTarget */ null, /* referrerFillInIntent = */ Intent() ) + } + + private fun createDisplayResolveInfo(isPinned: Boolean = false): DisplayResolveInfo = + DisplayResolveInfo.newDisplayResolveInfo( + Intent(), + ResolverDataProvider.createResolveInfo(2, 0, userHandle), + appLabel, + "extended info", + Intent(), + /* resolveInfoPresentationGetter= */ null + ) + .apply { + if (isPinned) { + setPinned(true) + } + } private fun createView(): View { val view = FrameLayout(context) diff --git a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java index 49305a6c..8608cf72 100644 --- a/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java +++ b/java/tests/src/com/android/intentresolver/ChooserWrapperActivity.java @@ -37,7 +37,6 @@ import androidx.lifecycle.ViewModelProvider; import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; -import com.android.intentresolver.dagger.TestViewModelComponent; import com.android.intentresolver.flags.FeatureFlagRepository; import com.android.intentresolver.grid.ChooserGridAdapter; import com.android.intentresolver.icons.TargetDataLoader; @@ -48,8 +47,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.List; import java.util.function.Consumer; -import javax.inject.Inject; - /** * Simple wrapper around chooser activity to be able to initiate it under test. For more * information, see {@code com.android.internal.app.ChooserWrapperActivity}. @@ -59,11 +56,6 @@ public class ChooserWrapperActivity static final ChooserActivityOverrideData sOverrides = ChooserActivityOverrideData.getInstance(); private UsageStatsManager mUsm; - @Inject - public ChooserWrapperActivity(TestViewModelComponent.Builder builder) { - super(builder); - } - // ResolverActivity (the base class of ChooserActivity) inspects the launched-from UID at // onCreate and needs to see some non-negative value in the test. @Override diff --git a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java index 11e7dffd..401ede26 100644 --- a/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java +++ b/java/tests/src/com/android/intentresolver/ResolverWrapperActivity.java @@ -44,8 +44,6 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Function; -import javax.inject.Inject; - /* * Simple wrapper around chooser activity to be able to initiate it under test */ @@ -55,7 +53,6 @@ public class ResolverWrapperActivity extends ResolverActivity { private final CountingIdlingResource mLabelIdlingResource = new CountingIdlingResource("LoadLabelTask"); - @Inject public ResolverWrapperActivity() { super(/* isIntentPicker= */ true); } diff --git a/java/tests/src/com/android/intentresolver/TestApplication.kt b/java/tests/src/com/android/intentresolver/TestApplication.kt index 4f5aefb9..849cfbab 100644 --- a/java/tests/src/com/android/intentresolver/TestApplication.kt +++ b/java/tests/src/com/android/intentresolver/TestApplication.kt @@ -16,13 +16,12 @@ package com.android.intentresolver +import android.app.Application import android.content.Context import android.os.UserHandle -import com.android.intentresolver.dagger.DaggerTestApplicationComponent -class TestApplication : IntentResolverApplication() { - override fun createApplicationComponentBuilder() = DaggerTestApplicationComponent.builder() +class TestApplication : Application() { // return the current context as a work profile doesn't really exist in these tests override fun createContextAsUser(user: UserHandle, flags: Int): Context = this -} +}
\ No newline at end of file diff --git a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java index ebc91701..aca78604 100644 --- a/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java +++ b/java/tests/src/com/android/intentresolver/UnbundledChooserActivityTest.java @@ -47,6 +47,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -496,7 +497,7 @@ public class UnbundledChooserActivityTest { String previewTitle = "My Content Preview Title"; Uri uri = Uri.parse( "android.resource://com.android.frameworks.coretests/" - + R.drawable.test320x240); + + com.android.intentresolver.tests.R.drawable.test320x240); Intent sendIntent = createSendTextIntentWithPreview(previewTitle, uri); ChooserActivityOverrideData.getInstance().imageLoader = createImageLoader(uri, createBitmap()); @@ -1940,11 +1941,13 @@ public class UnbundledChooserActivityTest { broadcastInvoked.countDown(); } }; - testContext.registerReceiver(testReceiver, new IntentFilter(testAction)); + testContext.registerReceiver(testReceiver, new IntentFilter(testAction), + Context.RECEIVER_EXPORTED); try { onView(withText(customActionLabel)).perform(click()); - broadcastInvoked.await(); + assertTrue("Timeout waiting for broadcast", + broadcastInvoked.await(5000, TimeUnit.MILLISECONDS)); } finally { testContext.unregisterReceiver(testReceiver); } @@ -1980,11 +1983,14 @@ public class UnbundledChooserActivityTest { broadcastInvoked.countDown(); } }; - testContext.registerReceiver(testReceiver, new IntentFilter(modifyShareAction)); + testContext.registerReceiver(testReceiver, new IntentFilter(modifyShareAction), + Context.RECEIVER_EXPORTED); try { onView(withText(label)).perform(click()); - broadcastInvoked.await(); + assertTrue("Timeout waiting for broadcast", + broadcastInvoked.await(5000, TimeUnit.MILLISECONDS)); + } finally { testContext.unregisterReceiver(testReceiver); } diff --git a/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt b/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt index b5fd1fa6..89978707 100644 --- a/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt +++ b/java/tests/src/com/android/intentresolver/contentpreview/ImagePreviewImageLoaderTest.kt @@ -55,6 +55,7 @@ import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import kotlinx.coroutines.yield import org.junit.After +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.Mockito.never @@ -301,7 +302,7 @@ class ImagePreviewImageLoaderTest { val latch = CountDownLatch(1) synchronized(pendingThumbnailCalls) { pendingThumbnailCalls.offer(latch) } thumbnailCallsCdl.countDown() - latch.await() + assertTrue("Timeout waiting thumbnail calls", latch.await(1, SECONDS)) bitmap } } diff --git a/java/tests/src/com/android/intentresolver/dagger/TestActivityBinderModule.kt b/java/tests/src/com/android/intentresolver/dagger/TestActivityBinderModule.kt deleted file mode 100644 index c08bc3b2..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestActivityBinderModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import android.app.Activity -import com.android.intentresolver.ChooserWrapperActivity -import com.android.intentresolver.ResolverWrapperActivity -import dagger.Binds -import dagger.Module -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -@Module -interface TestActivityBinderModule { - @Binds - @IntoMap - @ClassKey(ResolverWrapperActivity::class) - @ActivityScope - fun resolverWrapperActivity(activity: ResolverWrapperActivity): Activity - - @Binds - @IntoMap - @ClassKey(ChooserWrapperActivity::class) - @ActivityScope - fun chooserWrapperActivity(activity: ChooserWrapperActivity): Activity -} diff --git a/java/tests/src/com/android/intentresolver/dagger/TestActivityComponent.kt b/java/tests/src/com/android/intentresolver/dagger/TestActivityComponent.kt deleted file mode 100644 index 4416c852..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestActivityComponent.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import dagger.Subcomponent - -@ActivityScope -@Subcomponent( - modules = [ActivityModule::class, ActivityBinderModule::class, TestActivityBinderModule::class] -) -interface TestActivityComponent : ActivityComponent { - @Subcomponent.Factory - interface Factory : ActivityComponent.Factory { - override fun create(): TestActivityComponent - } -} diff --git a/java/tests/src/com/android/intentresolver/dagger/TestApplicationComponent.kt b/java/tests/src/com/android/intentresolver/dagger/TestApplicationComponent.kt deleted file mode 100644 index 224c46c6..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestApplicationComponent.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import android.app.Application -import dagger.BindsInstance -import dagger.Component -import javax.inject.Singleton - -@Singleton -@Component(modules = [TestApplicationModule::class]) -interface TestApplicationComponent : ApplicationComponent { - @Component.Builder - interface Builder : ApplicationComponent.Builder { - @BindsInstance - override fun application(application: Application): TestApplicationComponent.Builder - - override fun build(): TestApplicationComponent - } -} diff --git a/java/tests/src/com/android/intentresolver/dagger/TestApplicationModule.kt b/java/tests/src/com/android/intentresolver/dagger/TestApplicationModule.kt deleted file mode 100644 index 714748d2..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestApplicationModule.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import dagger.Binds -import dagger.Module -import javax.inject.Singleton - -@Module( - subcomponents = [TestActivityComponent::class, TestViewModelComponent::class], - includes = [ReceiverBinderModule::class, CoroutinesModule::class] -) -interface TestApplicationModule : ApplicationModule { - - /** Required to support field injection of [InjectedAppComponentFactory] */ - @Binds - @Singleton - fun activityComponent(component: TestActivityComponent.Factory): ActivityComponent.Factory - - /** Required to support injection into Activity instances */ - @Binds - @Singleton - fun viewModelComponent(component: TestViewModelComponent.Builder): ViewModelComponent.Builder -} diff --git a/java/tests/src/com/android/intentresolver/dagger/TestViewModelComponent.kt b/java/tests/src/com/android/intentresolver/dagger/TestViewModelComponent.kt deleted file mode 100644 index 539b3f36..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestViewModelComponent.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2023 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.dagger - -import dagger.Subcomponent - -/** A ViewModelComponent for tests which replaces ViewModelModule -> TestViewModelModule */ -@ViewModelScope -@Subcomponent(modules = [TestViewModelModule::class, ViewModelBinderModule::class]) -interface TestViewModelComponent : ViewModelComponent { - @Subcomponent.Builder - interface Builder : ViewModelComponent.Builder { - override fun build(): TestViewModelComponent - } -} diff --git a/java/tests/src/com/android/intentresolver/dagger/TestViewModelModule.kt b/java/tests/src/com/android/intentresolver/dagger/TestViewModelModule.kt deleted file mode 100644 index 28f4fa73..00000000 --- a/java/tests/src/com/android/intentresolver/dagger/TestViewModelModule.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.android.intentresolver.dagger - -import dagger.Module - -/** Provides bindings shared among components within [@ViewModelScope][ViewModelScope]. */ -@Module abstract class TestViewModelModule |