diff options
author | zachh <zachh@google.com> | 2018-03-28 03:03:17 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-03-28 03:03:17 +0000 |
commit | 225682f218ec61ab75d2e8d4af94bb21fdc3ce6f (patch) | |
tree | c67dc4f0884e4840a67d9556d2df3517401cdc49 | |
parent | 26f5a5d10317accad84afbb808109c55f8e0460d (diff) | |
parent | 0ed467d4f3367782d15499bc462f19a4035fdd5b (diff) |
Merge "Support new call log fragment in old peer." am: f17830ac72
am: 0ed467d4f3
Change-Id: I13b5e8c4ffcaf40feb6b9e5edc5b88477957bf4b
4 files changed, 182 insertions, 17 deletions
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java index 53ff43057..b86bfbc10 100644 --- a/java/com/android/dialer/calllog/CallLogFramework.java +++ b/java/com/android/dialer/calllog/CallLogFramework.java @@ -16,11 +16,17 @@ package com.android.dialer.calllog; +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.DataSources; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.Annotations.Ui; +import com.android.dialer.inject.ApplicationContext; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.List; @@ -35,13 +41,24 @@ import javax.inject.Singleton; @Singleton public final class CallLogFramework { + private final Context appContext; private final DataSources dataSources; private final AnnotatedCallLogMigrator annotatedCallLogMigrator; + private final ListeningExecutorService uiExecutor; + private final CallLogState callLogState; @Inject - CallLogFramework(DataSources dataSources, AnnotatedCallLogMigrator annotatedCallLogMigrator) { + CallLogFramework( + @ApplicationContext Context appContext, + DataSources dataSources, + AnnotatedCallLogMigrator annotatedCallLogMigrator, + @Ui ListeningExecutorService uiExecutor, + CallLogState callLogState) { + this.appContext = appContext; this.dataSources = dataSources; this.annotatedCallLogMigrator = annotatedCallLogMigrator; + this.uiExecutor = uiExecutor; + this.callLogState = callLogState; } /** Registers the content observers for all data sources. */ @@ -73,12 +90,25 @@ public final class CallLogFramework { dataSource.unregisterContentObservers(); } + callLogState.clearData(); + // Clear data only after all content observers have been disabled. List<ListenableFuture<Void>> allFutures = new ArrayList<>(); for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) { allFutures.add(dataSource.clearData()); } + return Futures.transform( - Futures.allAsList(allFutures), unused -> null, MoreExecutors.directExecutor()); + Futures.allAsList(allFutures), + unused -> { + // Send a broadcast to the OldMainActivityPeer to remove the NewCallLogFragment if it is + // currently attached. If this is not done, user interaction with the fragment could cause + // call log framework state to be unexpectedly written. For example scrolling could cause + // the AnnotatedCallLog to be read (which would trigger database creation). + LocalBroadcastManager.getInstance(appContext) + .sendBroadcastSync(new Intent("disableNewCallLogFragment")); + return null; + }, + uiExecutor); } } diff --git a/java/com/android/dialer/calllog/CallLogState.java b/java/com/android/dialer/calllog/CallLogState.java index bdb30a769..6d2ec1e5e 100644 --- a/java/com/android/dialer/calllog/CallLogState.java +++ b/java/com/android/dialer/calllog/CallLogState.java @@ -54,6 +54,15 @@ public final class CallLogState { } /** + * Clear the call log state. This is useful for example if the annotated call log needs to be + * disabled because there was a problem. + */ + @AnyThread + public void clearData() { + sharedPreferences.edit().remove(ANNOTATED_CALL_LOG_BUILT_PREF).apply(); + } + + /** * Returns true if the annotated call log has been built at least once. * * <p>It may not yet have been built if the user was just upgraded to the new call log, or they diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java index 9332acdb1..8362a81ac 100644 --- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java +++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java @@ -30,7 +30,6 @@ import android.provider.CallLog.Calls; import android.provider.VoicemailContract; import android.provider.VoicemailContract.Voicemails; import android.support.annotation.ColorInt; -import android.support.annotation.MainThread; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.annotation.VisibleForTesting; @@ -102,11 +101,8 @@ public class SystemCallLogDataSource implements CallLogDataSource { this.annotatedCallLogDatabaseHelper = annotatedCallLogDatabaseHelper; } - @MainThread @Override public void registerContentObservers() { - Assert.isMainThread(); - LogUtil.enterBlock("SystemCallLogDataSource.registerContentObservers"); if (!PermissionsUtil.hasCallLogReadPermissions(appContext)) { diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java index 9e8bd1ad6..6d78a5171 100644 --- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java +++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java @@ -21,9 +21,11 @@ import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.KeyguardManager; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; @@ -37,6 +39,7 @@ import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.telecom.PhoneAccount; @@ -65,6 +68,8 @@ import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.calldetails.OldCallDetailsActivity; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.callintent.CallSpecificAppData; +import com.android.dialer.calllog.config.CallLogConfigComponent; +import com.android.dialer.calllog.ui.NewCallLogFragment; import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutorComponent; @@ -110,7 +115,6 @@ import com.android.dialer.voicemailstatus.VoicemailStatusHelper; import com.android.voicemail.VoicemailComponent; import com.google.common.util.concurrent.ListenableFuture; import java.util.Locale; -import java.util.Objects; import java.util.concurrent.TimeUnit; /** @@ -132,6 +136,23 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen // TODO(calderwoodra): change to AppCompatActivity once new speed dial ships private final TransactionSafeActivity activity; + private final BroadcastReceiver disableNewCallLogReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (bottomNavTabListener == null) { + return; + } + /* + * Remove the NewCallLogFragment if it is currently attached. If this is not done, user + * interaction with the fragment could cause call log framework state to be unexpectedly + * written. For example scrolling could cause the AnnotatedCallLog to be read (which would + * trigger database creation). + */ + bottomNavTabListener.disableNewCallLogFragment(); + } + }; + // Contacts private MainOnContactSelectedListener onContactSelectedListener; @@ -161,6 +182,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private LastTabController lastTabController; private BottomNavBar bottomNav; + private MainBottomNavBarBottomNavTabListener bottomNavTabListener; private View snackbarContainer; private UiListener<String> getLastOutgoingCallListener; @@ -217,8 +239,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen activity.setSupportActionBar(activity.findViewById(R.id.toolbar)); bottomNav = activity.findViewById(R.id.bottom_nav_bar); - MainBottomNavBarBottomNavTabListener bottomNavTabListener = - new MainBottomNavBarBottomNavTabListener(activity, activity.getFragmentManager(), fab); + bottomNavTabListener = + new MainBottomNavBarBottomNavTabListener( + activity, activity.getFragmentManager(), activity.getSupportFragmentManager(), fab); bottomNav.addOnTabSelectedListener(bottomNavTabListener); // TODO(uabdullah): Handle case of when a sim is inserted/removed while the activity is open. boolean showVoicemailTab = canVoicemailTabBeShown(activity); @@ -432,6 +455,22 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen bottomNav.setVisibility(View.VISIBLE); } + /* + * While the activity is running, listen for the call log framework being disabled. If this is + * not done, user interaction with the fragment could cause call log framework state to be + * unexpectedly written. For example scrolling could cause the AnnotatedCallLog to be read + * (which would trigger database creation). + */ + LocalBroadcastManager.getInstance(activity) + .registerReceiver(disableNewCallLogReceiver, new IntentFilter("disableNewCallLogFragment")); + + /* + * Similar to above, if the new call log is being shown and then the activity is paused, when + * the user returns we need to remove the NewCallLogFragment if the framework has been disabled + * in the meantime. + */ + bottomNavTabListener.ensureCorrectCallLogShown(); + // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume. ThreadUtil.postDelayedOnUiThread( () -> @@ -449,6 +488,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onActivityPause() { searchController.onActivityPause(); + LocalBroadcastManager.getInstance(activity).unregisterReceiver(disableNewCallLogReceiver); } @Override @@ -1122,14 +1162,19 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private final AppCompatActivity activity; private final FragmentManager fragmentManager; + private final android.support.v4.app.FragmentManager supportFragmentManager; private final FloatingActionButton fab; @TabIndex private int selectedTab = -1; private MainBottomNavBarBottomNavTabListener( - AppCompatActivity activity, FragmentManager fragmentManager, FloatingActionButton fab) { + AppCompatActivity activity, + FragmentManager fragmentManager, + android.support.v4.app.FragmentManager supportFragmentManager, + FloatingActionButton fab) { this.activity = activity; this.fragmentManager = fragmentManager; + this.supportFragmentManager = supportFragmentManager; this.fab = fab; } @@ -1154,11 +1199,46 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen } Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_CALL_LOG, activity); selectedTab = TabIndex.CALL_LOG; - Fragment fragment = fragmentManager.findFragmentByTag(CALL_LOG_TAG); - showFragment(fragment == null ? new CallLogFragment() : fragment, CALL_LOG_TAG); + + if (CallLogConfigComponent.get(activity).callLogConfig().isNewCallLogFragmentEnabled()) { + android.support.v4.app.Fragment supportFragment = + supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + showSupportFragment( + supportFragment == null ? new NewCallLogFragment() : supportFragment, CALL_LOG_TAG); + } else { + Fragment fragment = fragmentManager.findFragmentByTag(CALL_LOG_TAG); + showFragment(fragment == null ? new CallLogFragment() : fragment, CALL_LOG_TAG); + } fab.show(); } + void disableNewCallLogFragment() { + LogUtil.i("MainBottomNavBarBottomNavTabListener.disableNewCallLogFragment", "disabled"); + android.support.v4.app.Fragment supportFragment = + supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (supportFragment != null) { + supportFragmentManager.beginTransaction().remove(supportFragment).commit(); + // If the NewCallLogFragment was showing, immediately show the old call log fragment + // instead. + if (selectedTab == TabIndex.CALL_LOG) { + LogUtil.i( + "MainBottomNavBarBottomNavTabListener.disableNewCallLogFragment", "showing old"); + Fragment fragment = fragmentManager.findFragmentByTag(CALL_LOG_TAG); + showFragment(fragment == null ? new CallLogFragment() : fragment, CALL_LOG_TAG); + } + } + } + + void ensureCorrectCallLogShown() { + android.support.v4.app.Fragment supportFragment = + supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (supportFragment != null + && !CallLogConfigComponent.get(activity).callLogConfig().isNewCallLogFragmentEnabled()) { + LogUtil.i("MainBottomNavBarBottomNavTabListener.ensureCorrectCallLogShown", "disabling"); + disableNewCallLogFragment(); + } + } + @Override public void onContactsSelected() { LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onContactsSelected"); @@ -1193,34 +1273,65 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen fragment.onVisible(); } + private void showFragment(@NonNull Fragment fragment, String tag) { + showFragment(fragment, null, tag); + } + /** * Shows the passed in fragment and hides all of the others in one transaction. * + * <p>Exactly one of fragment or supportFragment should be provided. + * * <p>Executes all fragment shows/hides in one transaction with no conflicting transactions * (like showing and hiding the same fragment in the same transaction). See a bug. * * <p>Special care should be taken to avoid calling this method several times in a short window * as it can lead to fragments overlapping. */ - private void showFragment(@NonNull Fragment fragment, String tag) { + private void showFragment( + @Nullable Fragment fragment, + @Nullable android.support.v4.app.Fragment supportFragment, + String tag) { LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.showFragment"); Fragment speedDial = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG); - Fragment callLog = fragmentManager.findFragmentByTag(CALL_LOG_TAG); + Fragment oldCallLog = fragmentManager.findFragmentByTag(CALL_LOG_TAG); Fragment contacts = fragmentManager.findFragmentByTag(CONTACTS_TAG); Fragment voicemail = fragmentManager.findFragmentByTag(VOICEMAIL_TAG); FragmentTransaction transaction = fragmentManager.beginTransaction(); boolean fragmentShown = showIfEqualElseHide(transaction, fragment, speedDial); - fragmentShown |= showIfEqualElseHide(transaction, fragment, callLog); + fragmentShown |= showIfEqualElseHide(transaction, fragment, oldCallLog); fragmentShown |= showIfEqualElseHide(transaction, fragment, contacts); fragmentShown |= showIfEqualElseHide(transaction, fragment, voicemail); - if (!fragmentShown) { + if (!fragmentShown && fragment != null) { LogUtil.i( "MainBottomNavBarBottomNavTabListener.showFragment", "Not added yet: " + fragment); transaction.add(R.id.fragment_container, fragment, tag); } transaction.commit(); + + // Handle support fragments. + // TODO(calderwoodra): Handle other new fragments. + android.support.v4.app.Fragment newCallLog = + supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + + android.support.v4.app.FragmentTransaction supportTransaction = + supportFragmentManager.beginTransaction(); + boolean supportFragmentShown = + showIfEqualElseHideSupport(supportTransaction, supportFragment, newCallLog); + if (!supportFragmentShown && supportFragment != null) { + LogUtil.i( + "MainBottomNavBarBottomNavTabListener.showFragment", + "Not added yet: " + supportFragment); + supportTransaction.add(R.id.fragment_container, supportFragment, tag); + } + supportTransaction.commit(); + } + + private void showSupportFragment( + @NonNull android.support.v4.app.Fragment supportFragment, String tag) { + showFragment(null, supportFragment, tag); } /** @@ -1231,7 +1342,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private boolean showIfEqualElseHide( FragmentTransaction transaction, Fragment fragment1, Fragment fragment2) { boolean shown = false; - if (Objects.equals(fragment1, fragment2)) { + if (fragment1 != null && fragment1.equals(fragment2)) { transaction.show(fragment1); shown = true; } else if (fragment2 != null) { @@ -1243,6 +1354,25 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen } return shown; } + + /** + * @param supportFragment1 will be shown if equal to {@code fragment2} + * @param supportFragment2 will be hidden if unequal to {@code fragment1} + * @return {@code true} if {@code fragment1} was shown + */ + private boolean showIfEqualElseHideSupport( + android.support.v4.app.FragmentTransaction supportTransaction, + android.support.v4.app.Fragment supportFragment1, + android.support.v4.app.Fragment supportFragment2) { + boolean shown = false; + if (supportFragment1 != null && supportFragment1.equals(supportFragment2)) { + supportTransaction.show(supportFragment1); + shown = true; + } else if (supportFragment2 != null) { + supportTransaction.hide(supportFragment2); + } + return shown; + } } private static final class LastTabController { |