1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.car.qc; 18 19 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 20 import static android.Manifest.permission.INTERNET; 21 import static android.widget.PopupWindow.INPUT_METHOD_NOT_NEEDED; 22 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.app.ActivityManager; 26 import android.app.ActivityTaskManager; 27 import android.app.TaskStackListener; 28 import android.car.drivingstate.CarUxRestrictions; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.SharedPreferences; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageInfo; 34 import android.content.pm.PackageManager; 35 import android.content.res.Resources; 36 import android.net.ConnectivityManager; 37 import android.net.Network; 38 import android.net.NetworkCapabilities; 39 import android.os.Build; 40 import android.os.Handler; 41 import android.text.TextUtils; 42 import android.util.Log; 43 import android.view.LayoutInflater; 44 import android.view.MotionEvent; 45 import android.view.View; 46 import android.widget.Button; 47 import android.widget.LinearLayout; 48 import android.widget.PopupWindow; 49 import android.widget.TextView; 50 51 import androidx.annotation.NonNull; 52 import androidx.annotation.VisibleForTesting; 53 54 import com.android.car.datasubscription.DataSubscription; 55 import com.android.car.datasubscription.DataSubscriptionStatus; 56 import com.android.car.ui.utils.CarUxRestrictionsUtil; 57 import com.android.systemui.R; 58 import com.android.systemui.car.qc.DataSubscriptionStatsLogHelper.DataSubscriptionMessageType; 59 import com.android.systemui.dagger.SysUISingleton; 60 import com.android.systemui.dagger.qualifiers.Background; 61 import com.android.systemui.dagger.qualifiers.Main; 62 import com.android.systemui.settings.UserTracker; 63 64 import java.time.LocalDate; 65 import java.time.ZoneId; 66 import java.time.format.DateTimeFormatter; 67 import java.time.temporal.ChronoUnit; 68 import java.util.Arrays; 69 import java.util.HashSet; 70 import java.util.List; 71 import java.util.Set; 72 import java.util.concurrent.CountDownLatch; 73 import java.util.concurrent.Executor; 74 import java.util.concurrent.TimeUnit; 75 76 import javax.inject.Inject; 77 78 /** 79 * Controller to display the data subscription pop-up 80 */ 81 @SysUISingleton 82 public class DataSubscriptionController implements DataSubscription.DataSubscriptionChangeListener { 83 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 84 private static final String TAG = DataSubscriptionController.class.toString(); 85 private static final String DATA_SUBSCRIPTION_ACTION = 86 "android.intent.action.DATA_SUBSCRIPTION"; 87 private static final String DATA_SUBSCRIPTION_SHARED_PREFERENCE_PATH = 88 "com.android.car.systemui.car.qc.DataSubscriptionController"; 89 // Timeout for network callback in ms 90 private static final int CALLBACK_TIMEOUT_MS = 1000; 91 private final Context mContext; 92 private DataSubscription mSubscription; 93 private final UserTracker mUserTracker; 94 private PopupWindow mPopupWindow; 95 private final View mPopupView; 96 private Button mExplorationButton; 97 private final Intent mIntent; 98 private ConnectivityManager mConnectivityManager; 99 private DataSubscriptionNetworkCallback mNetworkCallback; 100 private final Handler mMainHandler; 101 private final Executor mBackGroundExecutor; 102 private Set<String> mActivitiesBlocklist; 103 private Set<String> mPackagesBlocklist; 104 private CountDownLatch mLatch; 105 private boolean mIsNetworkCallbackRegistered; 106 private final DataSubscriptionStatsLogHelper mDataSubscriptionStatsLogHelper; 107 private final TaskStackListener mTaskStackListener = new TaskStackListener() { 108 @SuppressLint("MissingPermission") 109 @Override 110 public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { 111 if (mIsNetworkCallbackRegistered && mConnectivityManager != null) { 112 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); 113 mIsNetworkCallbackRegistered = false; 114 } 115 116 if (taskInfo.topActivity == null || mConnectivityManager == null) { 117 return; 118 } 119 mTopPackage = taskInfo.topActivity.getPackageName(); 120 if (mPackagesBlocklist.contains(mTopPackage)) { 121 return; 122 } 123 124 mTopActivity = taskInfo.topActivity.flattenToString(); 125 if (mActivitiesBlocklist.contains(mTopActivity)) { 126 return; 127 } 128 129 PackageInfo info; 130 int userId = mUserTracker.getUserId(); 131 try { 132 info = mContext.getPackageManager().getPackageInfoAsUser(mTopPackage, 133 PackageManager.GET_PERMISSIONS, userId); 134 if (info != null) { 135 String[] permissions = info.requestedPermissions; 136 boolean appReqInternet = Arrays.asList(permissions).contains( 137 ACCESS_NETWORK_STATE) 138 && Arrays.asList(permissions).contains(INTERNET); 139 if (!appReqInternet) { 140 mActivitiesBlocklist.add(mTopActivity); 141 return; 142 } 143 } 144 145 ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser( 146 mTopPackage, 0, mUserTracker.getUserId()); 147 mTopLabel = appInfo.loadLabel(mContext.getPackageManager()); 148 int uid = appInfo.uid; 149 mConnectivityManager.registerDefaultNetworkCallbackForUid(uid, mNetworkCallback, 150 mMainHandler); 151 mIsNetworkCallbackRegistered = true; 152 // since we don't have the option of using the synchronous call of getting the 153 // default network by UID, we need to set a timeout period to make sure the network 154 // from the callback is updated correctly before deciding to display the message 155 //TODO: b/336869328 use the synchronous call to update network status 156 mLatch = new CountDownLatch(CALLBACK_TIMEOUT_MS); 157 mBackGroundExecutor.execute(() -> { 158 try { 159 mLatch.await(CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); 160 } catch (InterruptedException e) { 161 Log.e(TAG, "error updating network callback" + e); 162 } finally { 163 if (mNetworkCallback.mNetwork == null) { 164 mNetworkCapabilities = null; 165 updateShouldDisplayReactiveMsg(); 166 } 167 } 168 }); 169 } catch (Exception e) { 170 Log.e(TAG, mTopPackage + " not found : " + e); 171 } 172 } 173 }; 174 175 private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener 176 mUxRestrictionsChangedListener = 177 new CarUxRestrictionsUtil.OnUxRestrictionsChangedListener() { 178 @Override 179 public void onRestrictionsChanged(@NonNull CarUxRestrictions carUxRestrictions) { 180 mIsDistractionOptimizationRequired = 181 carUxRestrictions.isRequiresDistractionOptimization(); 182 if (mIsProactiveMsg) { 183 if (mIsDistractionOptimizationRequired 184 && mPopupWindow != null 185 && mPopupWindow.isShowing()) { 186 mPopupWindow.dismiss(); 187 mDataSubscriptionStatsLogHelper.logSessionFinished(); 188 } 189 } else { 190 if (mIsDistractionOptimizationRequired && mPopupWindow != null) { 191 mExplorationButton.setVisibility(View.GONE); 192 193 } else { 194 mExplorationButton.setVisibility(View.VISIBLE); 195 } 196 mPopupWindow.update(); 197 } 198 } 199 }; 200 201 // Determines whether a proactive message was already displayed 202 private boolean mWasProactiveMsgDisplayed; 203 // Determines whether the current message being displayed is proactive or reactive 204 private boolean mIsProactiveMsg; 205 private boolean mIsDistractionOptimizationRequired; 206 private View mAnchorView; 207 private boolean mShouldDisplayProactiveMsg; 208 209 private final int mPopUpTimeOut; 210 private boolean mShouldDisplayReactiveMsg; 211 private String mTopActivity; 212 private String mTopPackage; 213 private CharSequence mTopLabel; 214 private NetworkCapabilities mNetworkCapabilities; 215 private boolean mIsUxRestrictionsListenerRegistered; 216 private SharedPreferences mSharedPreferences; 217 private SharedPreferences.Editor mEditor; 218 private int mCurrentInterval; 219 private int mCurrentCycle; 220 private int mCurrentActiveDays; 221 222 @VisibleForTesting 223 static final String KEY_PREV_POPUP_DATE = 224 "com.android.car.systemui.car.qc.PREV_DATE"; 225 @VisibleForTesting 226 static final String KEY_PREV_POPUP_CYCLE = 227 "com.android.car.systemui.car.qc.PREV_CYCLE"; 228 @VisibleForTesting 229 static final String KEY_PREV_POPUP_ACTIVE_DAYS = 230 "com.android.car.systemui.car.qc.PREV_ACTIVE_DAYS"; 231 @VisibleForTesting 232 static final String KEY_PREV_POPUP_STATUS = 233 "com.android.car.systemui.car.qc.PREV_STATUS"; 234 235 @SuppressLint("MissingPermission") 236 @Inject DataSubscriptionController(Context context, UserTracker userTracker, @Main Handler mainHandler, @Background Executor backgroundExecutor, DataSubscriptionStatsLogHelper dataSubscriptionStatsLogHelper)237 public DataSubscriptionController(Context context, 238 UserTracker userTracker, 239 @Main Handler mainHandler, 240 @Background Executor backgroundExecutor, 241 DataSubscriptionStatsLogHelper dataSubscriptionStatsLogHelper) { 242 mContext = context; 243 mSubscription = new DataSubscription(context); 244 mUserTracker = userTracker; 245 mMainHandler = mainHandler; 246 mBackGroundExecutor = backgroundExecutor; 247 mDataSubscriptionStatsLogHelper = dataSubscriptionStatsLogHelper; 248 mIntent = new Intent(DATA_SUBSCRIPTION_ACTION); 249 mIntent.setPackage(mContext.getString( 250 R.string.connectivity_flow_app)); 251 mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 252 LayoutInflater inflater = LayoutInflater.from(mContext); 253 mPopupView = inflater.inflate(R.layout.data_subscription_popup_window, null); 254 mPopUpTimeOut = mContext.getResources().getInteger( 255 R.integer.data_subscription_pop_up_timeout); 256 int width = LinearLayout.LayoutParams.WRAP_CONTENT; 257 int height = LinearLayout.LayoutParams.WRAP_CONTENT; 258 boolean focusable = true; 259 mPopupWindow = new PopupWindow(mPopupView, width, height, focusable); 260 mPopupWindow.setTouchModal(false); 261 mPopupWindow.setOutsideTouchable(true); 262 mPopupWindow.setInputMethodMode(INPUT_METHOD_NOT_NEEDED); 263 mPopupView.setOnTouchListener(new View.OnTouchListener() { 264 @Override 265 public boolean onTouch(View v, MotionEvent event) { 266 if (mPopupWindow != null) { 267 mPopupWindow.dismiss(); 268 if (!mWasProactiveMsgDisplayed) { 269 mWasProactiveMsgDisplayed = true; 270 } 271 mDataSubscriptionStatsLogHelper.logSessionFinished(); 272 } 273 return true; 274 } 275 }); 276 277 mExplorationButton = mPopupView.findViewById( 278 R.id.data_subscription_explore_options_button); 279 mExplorationButton.setOnClickListener(v -> { 280 mPopupWindow.dismiss(); 281 mContext.startActivityAsUser(mIntent, mUserTracker.getUserHandle()); 282 mDataSubscriptionStatsLogHelper.logButtonClicked(); 283 }); 284 mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); 285 mNetworkCallback = new DataSubscriptionNetworkCallback(); 286 mActivitiesBlocklist = new HashSet<>(); 287 mPackagesBlocklist = new HashSet<>(); 288 289 Resources res = mContext.getResources(); 290 String[] blockActivities = res.getStringArray( 291 R.array.config_dataSubscriptionBlockedActivitiesList); 292 mActivitiesBlocklist.addAll(List.of(blockActivities)); 293 String[] blockComponents = res.getStringArray( 294 R.array.config_dataSubscriptionBlockedPackagesList); 295 mPackagesBlocklist.addAll(List.of(blockComponents)); 296 try { 297 ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener); 298 } catch (Exception e) { 299 Log.e(TAG, "error while registering TaskStackListener " + e); 300 } 301 mSharedPreferences = mContext.getSharedPreferences( 302 DATA_SUBSCRIPTION_SHARED_PREFERENCE_PATH, Context.MODE_PRIVATE); 303 mEditor = mSharedPreferences.edit(); 304 } 305 updateShouldDisplayProactiveMsg()306 void updateShouldDisplayProactiveMsg() { 307 if (mIsDistractionOptimizationRequired) { 308 if (mPopupWindow != null && mPopupWindow.isShowing()) { 309 mPopupWindow.dismiss(); 310 mDataSubscriptionStatsLogHelper.logSessionFinished(); 311 } 312 } else { 313 // Determines whether a proactive message should be displayed 314 mShouldDisplayProactiveMsg = !mWasProactiveMsgDisplayed 315 && mSubscription.isDataSubscriptionInactive() 316 && isValidTimeInterval() 317 && isValidCycle() 318 && isValidActiveDays(); 319 if (mShouldDisplayProactiveMsg && mPopupWindow != null 320 && !mPopupWindow.isShowing()) { 321 mIsProactiveMsg = true; 322 showPopUpWindow(); 323 writeLatestPopupDate(); 324 writeLatestPopupCycle(); 325 writeLatestPopupActiveDays(); 326 } 327 } 328 } 329 updateShouldDisplayReactiveMsg()330 private void updateShouldDisplayReactiveMsg() { 331 if (mIsDistractionOptimizationRequired) { 332 mExplorationButton.setVisibility(View.GONE); 333 334 } else { 335 mExplorationButton.setVisibility(View.VISIBLE); 336 } 337 if (!mPopupWindow.isShowing()) { 338 mShouldDisplayReactiveMsg = ((mNetworkCapabilities == null 339 || (!isSuspendedNetwork() && !isValidNetwork())) 340 && mSubscription.isDataSubscriptionInactive()); 341 if (mShouldDisplayReactiveMsg) { 342 mIsProactiveMsg = false; 343 showPopUpWindow(); 344 mActivitiesBlocklist.add(mTopActivity); 345 } else { 346 if (mPopupWindow != null && mPopupWindow.isShowing()) { 347 mPopupWindow.dismiss(); 348 mDataSubscriptionStatsLogHelper.logSessionFinished(); 349 } 350 } 351 } 352 } 353 354 @VisibleForTesting showPopUpWindow()355 void showPopUpWindow() { 356 if (mAnchorView != null) { 357 mAnchorView.post(new Runnable() { 358 @Override 359 public void run() { 360 TextView popUpPrompt = mPopupView.findViewById(R.id.popup_text_view); 361 if (popUpPrompt != null) { 362 if (mIsProactiveMsg) { 363 popUpPrompt.setText(R.string.data_subscription_proactive_msg_prompt); 364 mDataSubscriptionStatsLogHelper.logSessionStarted( 365 DataSubscriptionMessageType.PROACTIVE); 366 } else { 367 popUpPrompt.setText(getReactiveMsg()); 368 mDataSubscriptionStatsLogHelper.logSessionStarted( 369 DataSubscriptionMessageType.REACTIVE); 370 } 371 } 372 int xOffsetInPx = mContext.getResources().getDimensionPixelSize( 373 R.dimen.data_subscription_pop_up_horizontal_offset); 374 int yOffsetInPx = mContext.getResources().getDimensionPixelSize( 375 R.dimen.data_subscription_pop_up_vertical_offset); 376 mPopupWindow.showAsDropDown(mAnchorView, -xOffsetInPx, yOffsetInPx); 377 mAnchorView.getHandler().postDelayed(new Runnable() { 378 379 public void run() { 380 if (mPopupWindow.isShowing()) { 381 mPopupWindow.dismiss(); 382 mWasProactiveMsgDisplayed = true; 383 // after the proactive msg dismisses, it won't get displayed again 384 // hence the msg from now on will just be reactive 385 mIsProactiveMsg = false; 386 mDataSubscriptionStatsLogHelper.logSessionFinished(); 387 } 388 } 389 }, mPopUpTimeOut); 390 } 391 }); 392 } 393 } 394 395 /** Set the anchor view. If null, unregisters active data subscription listeners */ setAnchorView(@ullable View view)396 public void setAnchorView(@Nullable View view) { 397 mAnchorView = view; 398 if (mAnchorView != null) { 399 mSubscription.addDataSubscriptionListener(this); 400 updateCurrentStatus(); 401 updateCurrentInterval(); 402 updateCurrentCycle(); 403 updateCurrentActiveDays(); 404 updateShouldDisplayProactiveMsg(); 405 if (!mIsUxRestrictionsListenerRegistered) { 406 CarUxRestrictionsUtil.getInstance(mContext).register( 407 mUxRestrictionsChangedListener); 408 mIsUxRestrictionsListenerRegistered = true; 409 } 410 } else { 411 mSubscription.removeDataSubscriptionListener(); 412 if (mIsUxRestrictionsListenerRegistered) { 413 CarUxRestrictionsUtil.getInstance(mContext).unregister( 414 mUxRestrictionsChangedListener); 415 mIsUxRestrictionsListenerRegistered = false; 416 } 417 } 418 } 419 isValidNetwork()420 boolean isValidNetwork() { 421 return mNetworkCapabilities.hasCapability( 422 NetworkCapabilities.NET_CAPABILITY_VALIDATED); 423 } 424 isSuspendedNetwork()425 boolean isSuspendedNetwork() { 426 return !mNetworkCapabilities.hasCapability( 427 NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); 428 } 429 getReactiveMsg()430 private CharSequence getReactiveMsg() { 431 return mContext.getString( 432 R.string.data_subscription_reactive_msg_prompt, mTopLabel.isEmpty() 433 ? mContext.getResources().getString( 434 R.string.data_subscription_reactive_generic_app_label) : 435 mTopLabel); 436 437 } 438 439 @Override onChange(int value)440 public void onChange(int value) { 441 updateCurrentStatus(); 442 updateShouldDisplayProactiveMsg(); 443 } 444 445 public class DataSubscriptionNetworkCallback extends ConnectivityManager.NetworkCallback { 446 Network mNetwork; 447 448 @Override onAvailable(@onNull Network network)449 public void onAvailable(@NonNull Network network) { 450 if (DEBUG) { 451 Log.d(TAG, "onAvailable " + network); 452 } 453 mNetwork = network; 454 mLatch.countDown(); 455 } 456 457 @Override onCapabilitiesChanged(@onNull Network network, @NonNull NetworkCapabilities networkCapabilities)458 public void onCapabilitiesChanged(@NonNull Network network, 459 @NonNull NetworkCapabilities networkCapabilities) { 460 if (DEBUG) { 461 Log.d(TAG, "onCapabilitiesChanged " + network); 462 } 463 mNetwork = network; 464 mNetworkCapabilities = networkCapabilities; 465 updateShouldDisplayReactiveMsg(); 466 } 467 } 468 isValidTimeInterval()469 private boolean isValidTimeInterval() { 470 return mCurrentInterval >= mContext.getResources().getInteger( 471 R.integer.data_subscription_pop_up_frequency); 472 } 473 isValidCycle()474 private boolean isValidCycle() { 475 if (mCurrentCycle == 1) { 476 return true; 477 } 478 return mCurrentCycle <= mContext.getResources().getInteger( 479 R.integer.data_subscription_pop_up_startup_cycle_limit); 480 } 481 isValidActiveDays()482 private boolean isValidActiveDays() { 483 if (mCurrentActiveDays == 1) { 484 return true; 485 } 486 return mCurrentActiveDays <= mContext.getResources().getInteger( 487 R.integer.data_subscription_pop_up_active_days_limit); 488 } 489 updateCurrentStatus()490 private void updateCurrentStatus() { 491 int prevStatus = mSharedPreferences.getInt(KEY_PREV_POPUP_STATUS, 0); 492 int currentStatus = mSubscription.getDataSubscriptionStatus(); 493 if (prevStatus == DataSubscriptionStatus.INACTIVE && prevStatus != currentStatus) { 494 mEditor.clear(); 495 mEditor.apply(); 496 } 497 mEditor.putInt(KEY_PREV_POPUP_STATUS, currentStatus); 498 mEditor.apply(); 499 } 500 updateCurrentInterval()501 private void updateCurrentInterval() { 502 mCurrentInterval = mContext.getResources().getInteger( 503 R.integer.data_subscription_pop_up_frequency); 504 String prevDate = mSharedPreferences.getString(KEY_PREV_POPUP_DATE, /* defValue=*/ ""); 505 if (!TextUtils.isEmpty(prevDate)) { 506 mCurrentInterval = (int) ChronoUnit.DAYS.between(LocalDate.parse(prevDate), 507 LocalDate.now(ZoneId.systemDefault())); 508 } 509 } 510 updateCurrentCycle()511 private void updateCurrentCycle() { 512 mCurrentCycle = mSharedPreferences.getInt( 513 KEY_PREV_POPUP_CYCLE, /* defValue=*/ 0); 514 } 515 updateCurrentActiveDays()516 private void updateCurrentActiveDays() { 517 mCurrentActiveDays = mSharedPreferences.getInt( 518 KEY_PREV_POPUP_ACTIVE_DAYS, /* defValue=*/ 0); 519 } 520 writeLatestPopupDate()521 private void writeLatestPopupDate() { 522 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 523 LocalDate newDate = LocalDate.now(ZoneId.systemDefault()); 524 String formattedNewDate = newDate.format(formatter); 525 mEditor.putString(KEY_PREV_POPUP_DATE, formattedNewDate); 526 mEditor.apply(); 527 } 528 writeLatestPopupCycle()529 private void writeLatestPopupCycle() { 530 mEditor.putInt(KEY_PREV_POPUP_CYCLE, mSharedPreferences.getInt( 531 KEY_PREV_POPUP_CYCLE, /* defValue=*/ 1) + 1); 532 mEditor.apply(); 533 } 534 writeLatestPopupActiveDays()535 private void writeLatestPopupActiveDays() { 536 mEditor.putInt(KEY_PREV_POPUP_ACTIVE_DAYS, mSharedPreferences.getInt( 537 KEY_PREV_POPUP_ACTIVE_DAYS, /* defValue=*/ 1) + 1); 538 mEditor.apply(); 539 } 540 541 @VisibleForTesting setPopupWindow(PopupWindow popupWindow)542 void setPopupWindow(PopupWindow popupWindow) { 543 mPopupWindow = popupWindow; 544 } 545 546 @VisibleForTesting setSubscription(DataSubscription dataSubscription)547 void setSubscription(DataSubscription dataSubscription) { 548 mSubscription = dataSubscription; 549 } 550 551 @VisibleForTesting getShouldDisplayProactiveMsg()552 boolean getShouldDisplayProactiveMsg() { 553 return mShouldDisplayProactiveMsg; 554 } 555 556 @VisibleForTesting setPackagesBlocklist(Set<String> list)557 void setPackagesBlocklist(Set<String> list) { 558 mPackagesBlocklist = list; 559 } 560 561 @VisibleForTesting setActivitiesBlocklist(Set<String> list)562 void setActivitiesBlocklist(Set<String> list) { 563 mActivitiesBlocklist = list; 564 } 565 566 @VisibleForTesting setConnectivityManager(ConnectivityManager connectivityManager)567 void setConnectivityManager(ConnectivityManager connectivityManager) { 568 mConnectivityManager = connectivityManager; 569 } 570 571 @VisibleForTesting getTaskStackListener()572 TaskStackListener getTaskStackListener() { 573 return mTaskStackListener; 574 } 575 576 @VisibleForTesting getShouldDisplayReactiveMsg()577 boolean getShouldDisplayReactiveMsg() { 578 return mShouldDisplayReactiveMsg; 579 } 580 581 @VisibleForTesting setNetworkCallback(DataSubscriptionNetworkCallback callback)582 void setNetworkCallback(DataSubscriptionNetworkCallback callback) { 583 mNetworkCallback = callback; 584 } 585 586 @VisibleForTesting setIsCallbackRegistered(boolean value)587 void setIsCallbackRegistered(boolean value) { 588 mIsNetworkCallbackRegistered = value; 589 } 590 591 @VisibleForTesting setIsProactiveMsg(boolean value)592 void setIsProactiveMsg(boolean value) { 593 mIsProactiveMsg = value; 594 } 595 596 @VisibleForTesting setExplorationButton(Button button)597 void setExplorationButton(Button button) { 598 mExplorationButton = button; 599 } 600 601 @VisibleForTesting setIsUxRestrictionsListenerRegistered(boolean value)602 void setIsUxRestrictionsListenerRegistered(boolean value) { 603 mIsUxRestrictionsListenerRegistered = value; 604 } 605 606 @VisibleForTesting setSharedPreference(SharedPreferences sharedPreference)607 void setSharedPreference(SharedPreferences sharedPreference) { 608 mSharedPreferences = sharedPreference; 609 } 610 611 @VisibleForTesting setCurrentInterval(int currentInterval)612 void setCurrentInterval(int currentInterval) { 613 mCurrentInterval = currentInterval; 614 } 615 616 @VisibleForTesting setCurrentCycle(int cycle)617 void setCurrentCycle(int cycle) { 618 mCurrentCycle = cycle; 619 } 620 621 @VisibleForTesting setCurrentActiveDays(int activeDays)622 void setCurrentActiveDays(int activeDays) { 623 mCurrentActiveDays = activeDays; 624 } 625 626 @VisibleForTesting setWasProactiveMsgDisplayed(boolean value)627 void setWasProactiveMsgDisplayed(boolean value) { 628 mWasProactiveMsgDisplayed = value; 629 } 630 } 631