Loading packages/SystemUI/res/layout/people_space_activity_no_conversations.xml +0 −1 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,6 @@ android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_gravity="center" android:background="@drawable/rounded_bg_full_large_radius" android:background="@drawable/rounded_bg_full_large_radius" android:onClick="dismissActivity" android:text="@string/got_it" android:text="@string/got_it" android:textColor="?androidprv:attr/textColorOnAccent" android:textColor="?androidprv:attr/textColorOnAccent" android:layout_marginBottom="60dp" android:layout_marginBottom="60dp" Loading packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +16 −24 Original line number Original line Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import android.content.Intent; import android.content.Intent; import android.os.Bundle; import android.os.Bundle; import android.util.Log; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import androidx.activity.ComponentActivity; import androidx.activity.ComponentActivity; Loading @@ -40,7 +39,6 @@ public class PeopleSpaceActivity extends ComponentActivity { private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private final PeopleViewModel.Factory mViewModelFactory; private final PeopleViewModel.Factory mViewModelFactory; private PeopleViewModel mViewModel; @Inject @Inject public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) { public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) { Loading @@ -52,38 +50,32 @@ public class PeopleSpaceActivity extends ComponentActivity { protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.onCreate(savedInstanceState); setResult(RESULT_CANCELED); setResult(RESULT_CANCELED); mViewModel = new ViewModelProvider(this, mViewModelFactory).get(PeopleViewModel.class); PeopleViewModel viewModel = new ViewModelProvider(this, mViewModelFactory).get( PeopleViewModel.class); // Update the widget ID coming from the intent. // Update the widget ID coming from the intent. int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); mViewModel.onWidgetIdChanged(widgetId); viewModel.onWidgetIdChanged(widgetId); ViewGroup view = PeopleViewBinder.create(this); ViewGroup view = PeopleViewBinder.create(this); PeopleViewBinder.bind(view, mViewModel, /* lifecycleOwner= */ this, PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this, () -> { (result) -> { finishActivity(); finishActivity(result); return null; return null; }); }); setContentView(view); setContentView(view); } } /** Finish activity with a successful widget configuration result. */ private void finishActivity(PeopleViewModel.Result result) { private void finishActivity() { if (result instanceof PeopleViewModel.Result.Success) { if (DEBUG) Log.d(TAG, "Widget added!"); if (DEBUG) Log.d(TAG, "Widget added!"); setActivityResult(RESULT_OK); Intent data = ((PeopleViewModel.Result.Success) result).getData(); finish(); setResult(RESULT_OK, data); } } else { /** Finish activity without choosing a widget. */ public void dismissActivity(View v) { if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); setResult(RESULT_CANCELED); setResult(RESULT_CANCELED); finish(); } } finish(); private void setActivityResult(int result) { Intent resultValue = new Intent(); resultValue.putExtra(EXTRA_APPWIDGET_ID, mViewModel.getAppWidgetId().getValue()); setResult(result, resultValue); } } } } packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt +13 −9 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,7 @@ import kotlinx.coroutines.launch /** A ViewBinder for [PeopleViewModel]. */ /** A ViewBinder for [PeopleViewModel]. */ object PeopleViewBinder { object PeopleViewBinder { private const val TAG = "PeopleSpaceViewBinder" private const val TAG = "PeopleViewBinder" /** /** * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. Loading Loading @@ -72,15 +72,15 @@ object PeopleViewBinder { view: ViewGroup, view: ViewGroup, viewModel: PeopleViewModel, viewModel: PeopleViewModel, lifecycleOwner: LifecycleOwner, lifecycleOwner: LifecycleOwner, onFinish: () -> Unit, onResult: (PeopleViewModel.Result) -> Unit, ) { ) { // Call [onFinish] this activity when the ViewModel tells us so. // Call [onResult] as soon as a result is available. lifecycleOwner.lifecycleScope.launch { lifecycleOwner.lifecycleScope.launch { lifecycleOwner.repeatOnLifecycle(CREATED) { lifecycleOwner.repeatOnLifecycle(CREATED) { viewModel.isFinished.collect { isFinished -> viewModel.result.collect { result -> if (isFinished) { if (result != null) { viewModel.clearIsFinished() viewModel.clearResult() onFinish() onResult(result) } } } } } } Loading @@ -104,7 +104,7 @@ object PeopleViewBinder { viewModel::onTileClicked, viewModel::onTileClicked, ) ) } else { } else { setNoConversationsContent(view) setNoConversationsContent(view, viewModel::onUserJourneyCancelled) } } } } } } Loading @@ -119,7 +119,7 @@ object PeopleViewBinder { } } } } private fun setNoConversationsContent(view: ViewGroup) { private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) { // This should never happen. // This should never happen. if (view.childCount > 1) { if (view.childCount > 1) { error("view has ${view.childCount} children, it should have maximum 1") error("view has ${view.childCount} children, it should have maximum 1") Loading @@ -140,6 +140,10 @@ object PeopleViewBinder { LayoutInflater.from(context) LayoutInflater.from(context) .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) noConversationsView.findViewById<View>(R.id.got_it_button).setOnClickListener { onGotItClicked() } // The Tile preview has colorBackground as its background. Change it so it's different than // The Tile preview has colorBackground as its background. Change it so it's different than // the activity's background. // the activity's background. val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background) val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background) Loading packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt +28 −14 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.systemui.people.ui.viewmodel package com.android.systemui.people.ui.viewmodel import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.content.Context import android.content.Context import android.content.Intent import android.util.Log import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider Loading @@ -32,6 +34,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** /** * Models UI state for the people space, allowing the user to select which conversation should be * Models UI state for the people space, allowing the user to select which conversation should be Loading @@ -49,7 +52,7 @@ class PeopleViewModel( * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. */ */ private val _priorityTiles = MutableStateFlow(priorityTiles()) private val _priorityTiles = MutableStateFlow(priorityTiles()) val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles.asStateFlow() /** /** * The list of the priority tiles/conversations. * The list of the priority tiles/conversations. Loading @@ -58,15 +61,15 @@ class PeopleViewModel( * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. */ */ private val _recentTiles = MutableStateFlow(recentTiles()) private val _recentTiles = MutableStateFlow(recentTiles()) val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles.asStateFlow() /** The ID of the widget currently being edited/added. */ /** The ID of the widget currently being edited/added. */ private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID) private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID) val appWidgetId: StateFlow<Int> = _appWidgetId val appWidgetId: StateFlow<Int> = _appWidgetId.asStateFlow() /** Whether the user journey is complete. */ /** The result of this user journey. */ private val _isFinished = MutableStateFlow(false) private val _result = MutableStateFlow<Result?>(null) val isFinished: StateFlow<Boolean> = _isFinished val result: StateFlow<Result?> = _result.asStateFlow() /** Refresh the [priorityTiles] and [recentTiles]. */ /** Refresh the [priorityTiles] and [recentTiles]. */ fun onTileRefreshRequested() { fun onTileRefreshRequested() { Loading @@ -79,22 +82,28 @@ class PeopleViewModel( _appWidgetId.value = widgetId _appWidgetId.value = widgetId } } /** Clear [isFinished], setting it to false. */ /** Clear [result], setting it to null. */ fun clearIsFinished() { fun clearResult() { _isFinished.value = false _result.value = null } } /** Called when a tile is clicked. */ /** Called when a tile is clicked. */ fun onTileClicked(tile: PeopleTileViewModel) { fun onTileClicked(tile: PeopleTileViewModel) { val widgetId = _appWidgetId.value if (PeopleSpaceUtils.DEBUG) { if (PeopleSpaceUtils.DEBUG) { Log.d( Log.d( TAG, TAG, "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID: " + "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID $widgetId" _appWidgetId.value ) ) } } widgetRepository.setWidgetTile(_appWidgetId.value, tile.key) widgetRepository.setWidgetTile(widgetId, tile.key) _isFinished.value = true _result.value = Result.Success(Intent().apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId.value) }) } /** Called when this user journey is cancelled. */ fun onUserJourneyCancelled() { _result.value = Result.Cancelled } } private fun priorityTiles(): List<PeopleTileViewModel> { private fun priorityTiles(): List<PeopleTileViewModel> { Loading Loading @@ -143,7 +152,12 @@ class PeopleViewModel( } } } } sealed class Result { class Success(val data: Intent) : Result() object Cancelled : Result() } companion object { companion object { private const val TAG = "PeopleSpaceViewModel" private const val TAG = "PeopleViewModel" } } } } Loading
packages/SystemUI/res/layout/people_space_activity_no_conversations.xml +0 −1 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,6 @@ android:layout_height="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_gravity="center" android:background="@drawable/rounded_bg_full_large_radius" android:background="@drawable/rounded_bg_full_large_radius" android:onClick="dismissActivity" android:text="@string/got_it" android:text="@string/got_it" android:textColor="?androidprv:attr/textColorOnAccent" android:textColor="?androidprv:attr/textColorOnAccent" android:layout_marginBottom="60dp" android:layout_marginBottom="60dp" Loading
packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +16 −24 Original line number Original line Diff line number Diff line Loading @@ -22,7 +22,6 @@ import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import android.content.Intent; import android.content.Intent; import android.os.Bundle; import android.os.Bundle; import android.util.Log; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup; import androidx.activity.ComponentActivity; import androidx.activity.ComponentActivity; Loading @@ -40,7 +39,6 @@ public class PeopleSpaceActivity extends ComponentActivity { private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private final PeopleViewModel.Factory mViewModelFactory; private final PeopleViewModel.Factory mViewModelFactory; private PeopleViewModel mViewModel; @Inject @Inject public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) { public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) { Loading @@ -52,38 +50,32 @@ public class PeopleSpaceActivity extends ComponentActivity { protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.onCreate(savedInstanceState); setResult(RESULT_CANCELED); setResult(RESULT_CANCELED); mViewModel = new ViewModelProvider(this, mViewModelFactory).get(PeopleViewModel.class); PeopleViewModel viewModel = new ViewModelProvider(this, mViewModelFactory).get( PeopleViewModel.class); // Update the widget ID coming from the intent. // Update the widget ID coming from the intent. int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); mViewModel.onWidgetIdChanged(widgetId); viewModel.onWidgetIdChanged(widgetId); ViewGroup view = PeopleViewBinder.create(this); ViewGroup view = PeopleViewBinder.create(this); PeopleViewBinder.bind(view, mViewModel, /* lifecycleOwner= */ this, PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this, () -> { (result) -> { finishActivity(); finishActivity(result); return null; return null; }); }); setContentView(view); setContentView(view); } } /** Finish activity with a successful widget configuration result. */ private void finishActivity(PeopleViewModel.Result result) { private void finishActivity() { if (result instanceof PeopleViewModel.Result.Success) { if (DEBUG) Log.d(TAG, "Widget added!"); if (DEBUG) Log.d(TAG, "Widget added!"); setActivityResult(RESULT_OK); Intent data = ((PeopleViewModel.Result.Success) result).getData(); finish(); setResult(RESULT_OK, data); } } else { /** Finish activity without choosing a widget. */ public void dismissActivity(View v) { if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); setResult(RESULT_CANCELED); setResult(RESULT_CANCELED); finish(); } } finish(); private void setActivityResult(int result) { Intent resultValue = new Intent(); resultValue.putExtra(EXTRA_APPWIDGET_ID, mViewModel.getAppWidgetId().getValue()); setResult(result, resultValue); } } } }
packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt +13 −9 Original line number Original line Diff line number Diff line Loading @@ -41,7 +41,7 @@ import kotlinx.coroutines.launch /** A ViewBinder for [PeopleViewModel]. */ /** A ViewBinder for [PeopleViewModel]. */ object PeopleViewBinder { object PeopleViewBinder { private const val TAG = "PeopleSpaceViewBinder" private const val TAG = "PeopleViewBinder" /** /** * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. Loading Loading @@ -72,15 +72,15 @@ object PeopleViewBinder { view: ViewGroup, view: ViewGroup, viewModel: PeopleViewModel, viewModel: PeopleViewModel, lifecycleOwner: LifecycleOwner, lifecycleOwner: LifecycleOwner, onFinish: () -> Unit, onResult: (PeopleViewModel.Result) -> Unit, ) { ) { // Call [onFinish] this activity when the ViewModel tells us so. // Call [onResult] as soon as a result is available. lifecycleOwner.lifecycleScope.launch { lifecycleOwner.lifecycleScope.launch { lifecycleOwner.repeatOnLifecycle(CREATED) { lifecycleOwner.repeatOnLifecycle(CREATED) { viewModel.isFinished.collect { isFinished -> viewModel.result.collect { result -> if (isFinished) { if (result != null) { viewModel.clearIsFinished() viewModel.clearResult() onFinish() onResult(result) } } } } } } Loading @@ -104,7 +104,7 @@ object PeopleViewBinder { viewModel::onTileClicked, viewModel::onTileClicked, ) ) } else { } else { setNoConversationsContent(view) setNoConversationsContent(view, viewModel::onUserJourneyCancelled) } } } } } } Loading @@ -119,7 +119,7 @@ object PeopleViewBinder { } } } } private fun setNoConversationsContent(view: ViewGroup) { private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) { // This should never happen. // This should never happen. if (view.childCount > 1) { if (view.childCount > 1) { error("view has ${view.childCount} children, it should have maximum 1") error("view has ${view.childCount} children, it should have maximum 1") Loading @@ -140,6 +140,10 @@ object PeopleViewBinder { LayoutInflater.from(context) LayoutInflater.from(context) .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) noConversationsView.findViewById<View>(R.id.got_it_button).setOnClickListener { onGotItClicked() } // The Tile preview has colorBackground as its background. Change it so it's different than // The Tile preview has colorBackground as its background. Change it so it's different than // the activity's background. // the activity's background. val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background) val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background) Loading
packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt +28 −14 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,10 @@ package com.android.systemui.people.ui.viewmodel package com.android.systemui.people.ui.viewmodel import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID import android.content.Context import android.content.Context import android.content.Intent import android.util.Log import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider Loading @@ -32,6 +34,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** /** * Models UI state for the people space, allowing the user to select which conversation should be * Models UI state for the people space, allowing the user to select which conversation should be Loading @@ -49,7 +52,7 @@ class PeopleViewModel( * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. */ */ private val _priorityTiles = MutableStateFlow(priorityTiles()) private val _priorityTiles = MutableStateFlow(priorityTiles()) val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles.asStateFlow() /** /** * The list of the priority tiles/conversations. * The list of the priority tiles/conversations. Loading @@ -58,15 +61,15 @@ class PeopleViewModel( * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. */ */ private val _recentTiles = MutableStateFlow(recentTiles()) private val _recentTiles = MutableStateFlow(recentTiles()) val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles.asStateFlow() /** The ID of the widget currently being edited/added. */ /** The ID of the widget currently being edited/added. */ private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID) private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID) val appWidgetId: StateFlow<Int> = _appWidgetId val appWidgetId: StateFlow<Int> = _appWidgetId.asStateFlow() /** Whether the user journey is complete. */ /** The result of this user journey. */ private val _isFinished = MutableStateFlow(false) private val _result = MutableStateFlow<Result?>(null) val isFinished: StateFlow<Boolean> = _isFinished val result: StateFlow<Result?> = _result.asStateFlow() /** Refresh the [priorityTiles] and [recentTiles]. */ /** Refresh the [priorityTiles] and [recentTiles]. */ fun onTileRefreshRequested() { fun onTileRefreshRequested() { Loading @@ -79,22 +82,28 @@ class PeopleViewModel( _appWidgetId.value = widgetId _appWidgetId.value = widgetId } } /** Clear [isFinished], setting it to false. */ /** Clear [result], setting it to null. */ fun clearIsFinished() { fun clearResult() { _isFinished.value = false _result.value = null } } /** Called when a tile is clicked. */ /** Called when a tile is clicked. */ fun onTileClicked(tile: PeopleTileViewModel) { fun onTileClicked(tile: PeopleTileViewModel) { val widgetId = _appWidgetId.value if (PeopleSpaceUtils.DEBUG) { if (PeopleSpaceUtils.DEBUG) { Log.d( Log.d( TAG, TAG, "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID: " + "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID $widgetId" _appWidgetId.value ) ) } } widgetRepository.setWidgetTile(_appWidgetId.value, tile.key) widgetRepository.setWidgetTile(widgetId, tile.key) _isFinished.value = true _result.value = Result.Success(Intent().apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId.value) }) } /** Called when this user journey is cancelled. */ fun onUserJourneyCancelled() { _result.value = Result.Cancelled } } private fun priorityTiles(): List<PeopleTileViewModel> { private fun priorityTiles(): List<PeopleTileViewModel> { Loading Loading @@ -143,7 +152,12 @@ class PeopleViewModel( } } } } sealed class Result { class Success(val data: Intent) : Result() object Cancelled : Result() } companion object { companion object { private const val TAG = "PeopleSpaceViewModel" private const val TAG = "PeopleViewModel" } } } }