diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4c9005a5b6bad..bf9b809e56841 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -572,6 +572,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml new file mode 100644 index 0000000000000..67ecdaa5d7b60 --- /dev/null +++ b/packages/SystemUI/res/layout/people_space_activity.xml @@ -0,0 +1,45 @@ + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml new file mode 100644 index 0000000000000..80bb07070b31b --- /dev/null +++ b/packages/SystemUI/res/layout/people_space_tile_view.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2427d36076c58..721dd1951be52 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2834,4 +2834,10 @@ Build number Build number copied to clipboard. + + + You last chatted %1$s ago + + Open conversation + diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java new file mode 100644 index 0000000000000..eeb93bb7d766a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2020 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.systemui.people; + +import android.app.Activity; +import android.app.INotificationManager; +import android.app.people.IPeopleManager; +import android.content.Context; +import android.content.pm.LauncherApps; +import android.content.pm.PackageManager; +import android.content.pm.ShortcutInfo; +import android.icu.text.MeasureFormat; +import android.icu.util.Measure; +import android.icu.util.MeasureUnit; +import android.os.Bundle; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.service.notification.ConversationChannelWrapper; +import android.util.Log; +import android.view.ViewGroup; + +import com.android.systemui.R; + +import java.time.Duration; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +/** + * Shows the user their tiles for their priority People (go/live-status). + */ +public class PeopleSpaceActivity extends Activity { + + private static String sTAG = "PeopleSpaceActivity"; + + private ViewGroup mPeopleSpaceLayout; + private IPeopleManager mPeopleManager; + private INotificationManager mNotificationManager; + private PackageManager mPackageManager; + private LauncherApps mLauncherApps; + private List mConversations; + private Context mContext; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.people_space_activity); + mPeopleSpaceLayout = findViewById(R.id.people_space_layout); + mContext = getApplicationContext(); + mNotificationManager = + INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mPackageManager = getPackageManager(); + mPeopleManager = IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); + mLauncherApps = mContext.getSystemService(LauncherApps.class); + setTileViewsWithPriorityConversations(); + } + + /** + * Retrieves all priority conversations and sets a {@link PeopleSpaceTileView}s for each + * priority conversation. + */ + private void setTileViewsWithPriorityConversations() { + try { + List conversations = + mNotificationManager.getConversations( + true /* priority only */).getList(); + mConversations = conversations.stream().filter( + c -> shouldKeepConversation(c)).collect(Collectors.toList()); + for (ConversationChannelWrapper conversation : mConversations) { + PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, + mPeopleSpaceLayout, + conversation.getShortcutInfo().getId()); + setTileView(tileView, conversation); + } + } catch (Exception e) { + Log.e(sTAG, "Couldn't retrieve conversations", e); + } + } + + /** Sets {@code tileView} with the data in {@code conversation}. */ + private void setTileView(PeopleSpaceTileView tileView, + ConversationChannelWrapper conversation) { + try { + ShortcutInfo shortcutInfo = conversation.getShortcutInfo(); + int userId = UserHandle.getUserHandleForUid( + conversation.getUid()).getIdentifier(); + + String pkg = shortcutInfo.getPackage(); + long lastInteraction = mPeopleManager.getLastInteraction( + pkg, userId, + shortcutInfo.getId()); + String status = lastInteraction != 0l ? mContext.getString( + R.string.last_interaction_status, + getLastInteractionString( + lastInteraction)) : mContext.getString(R.string.basic_status); + tileView.setStatus(status); + + tileView.setName(shortcutInfo.getLabel().toString()); + tileView.setPackageIcon(mPackageManager.getApplicationIcon(pkg)); + tileView.setPersonIcon(mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)); + tileView.setOnClickListener(mLauncherApps, shortcutInfo); + } catch (Exception e) { + Log.e(sTAG, "Couldn't retrieve shortcut information", e); + } + } + + /** Returns a readable representation of {@code lastInteraction}. */ + private String getLastInteractionString(long lastInteraction) { + long now = System.currentTimeMillis(); + Duration durationSinceLastInteraction = Duration.ofMillis( + now - lastInteraction); + MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(), + MeasureFormat.FormatWidth.WIDE); + if (durationSinceLastInteraction.toDays() >= 1) { + return + formatter + .formatMeasures(new Measure(durationSinceLastInteraction.toDays(), + MeasureUnit.DAY)); + } else if (durationSinceLastInteraction.toHours() >= 1) { + return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toHours(), + MeasureUnit.HOUR)); + } else if (durationSinceLastInteraction.toMinutes() >= 1) { + return formatter.formatMeasures(new Measure(durationSinceLastInteraction.toMinutes(), + MeasureUnit.MINUTE)); + } else { + return formatter.formatMeasures( + new Measure(durationSinceLastInteraction.toMillis() / 1000, + MeasureUnit.SECOND)); + } + } + + /** + * Returns whether the {@code conversation} should be kept for display in the People Space. + * + *

A valid {@code conversation} must: + *

    + *
  • Have a non-null {@link ShortcutInfo} + *
  • Have an associated label in the {@link ShortcutInfo} + *
+ * + */ + private boolean shouldKeepConversation(ConversationChannelWrapper conversation) { + ShortcutInfo shortcutInfo = conversation.getShortcutInfo(); + return shortcutInfo != null && shortcutInfo.getLabel().length() != 0; + } + + @Override + protected void onResume() { + super.onResume(); + // Refresh tile views to sync new conversations. + setTileViewsWithPriorityConversations(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java new file mode 100644 index 0000000000000..d5ef190d5ff1a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 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.systemui.people; + +import android.content.Context; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.systemui.R; + +/** + * PeopleSpaceTileView renders an individual person's tile with associated status. + */ +public class PeopleSpaceTileView extends LinearLayout { + + private View mTileView; + private TextView mNameView; + private TextView mStatusView; + private ImageView mPackageIconView; + private ImageView mPersonIconView; + + public PeopleSpaceTileView(Context context, ViewGroup view, String shortcutId) { + super(context); + mTileView = view.findViewWithTag(shortcutId); + if (mTileView == null) { + LayoutInflater inflater = LayoutInflater.from(context); + mTileView = inflater.inflate(R.layout.people_space_tile_view, view, false); + view.addView(mTileView, LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + mTileView.setTag(shortcutId); + } + mNameView = mTileView.findViewById(R.id.tile_view_name); + mStatusView = mTileView.findViewById(R.id.tile_view_status); + mPackageIconView = mTileView.findViewById(R.id.tile_view_package_icon); + mPersonIconView = mTileView.findViewById(R.id.tile_view_person_icon); + } + + /** Sets the name text on the tile. */ + public void setName(String name) { + mNameView.setText(name); + } + + /** Sets the status text on the tile. */ + public void setStatus(String status) { + mStatusView.setText(status); + } + + /** Sets the package drawable on the tile. */ + public void setPackageIcon(Drawable drawable) { + mPackageIconView.setImageDrawable(drawable); + } + + /** Sets the person drawable on the tile. */ + public void setPersonIcon(Drawable drawable) { + mPersonIconView.setImageDrawable(drawable); + } + + /** Sets the click listener of the tile. */ + public void setOnClickListener(LauncherApps launcherApps, ShortcutInfo shortcutInfo) { + mTileView.setOnClickListener(v -> launcherApps.startShortcut(shortcutInfo, null, null)); + } +}