Merge "Create & populate People Space activity"

This commit is contained in:
Anna Zappone
2020-10-07 17:45:33 +00:00
committed by Android (Google) Code Review
7 changed files with 403 additions and 0 deletions

View File

@@ -572,6 +572,13 @@
</intent-filter>
</activity>
<!-- People Space UI Screen -->
<activity
android:name=".people.PeopleSpaceActivity"
android:exported="true"
android:theme="@android:style/Theme.Material.NoActionBar">
</activity>
<!-- a gallery of delicious treats -->
<service
android:name=".DessertCaseDream"

View File

@@ -0,0 +1,28 @@
<!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<padding
android:left="1dp"
android:top="1dp"
android:right="1dp"
android:bottom="1dp" />
<corners
android:bottomRightRadius="7dp"
android:bottomLeftRadius="7dp"
android:topRightRadius="7dp"
android:topLeftRadius="7dp" />
</shape>

View File

@@ -0,0 +1,45 @@
<!--
~ 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.
-->
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
<LinearLayout
android:id="@+id/people_space_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:padding="16dp"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/people_space_cloud"
android:layout_width="67dp"
android:layout_height="67dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
android:focusable="false"
android:paddingBottom="16dp"
android:tint="?android:attr/colorControlNormal"
android:src="@drawable/cloud" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tile_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
android:orientation="vertical"
android:padding="16dp"
android:layout_marginBottom="24dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:gravity="start">
<ImageView
android:id="@+id/tile_view_package_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end" />
<ImageView
android:id="@+id/tile_view_person_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="start" />
<TextView
android:id="@+id/tile_view_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="4dp"
android:textSize="22sp"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start" />
<TextView
android:id="@+id/tile_view_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="4dp"
android:textSize="16sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:layout_gravity="start" />
</LinearLayout>
</LinearLayout>

View File

@@ -2834,4 +2834,10 @@
<string name="build_number_clip_data_label">Build number</string>
<!-- Text to display when copying the build number off QS [CHAR LIMIT=NONE]-->
<string name="build_number_copy_toast">Build number copied to clipboard.</string>
<!-- Status for last interaction [CHAR LIMIT=120] -->
<string name="last_interaction_status" translatable="false">You last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="basic_status" translatable="false">Open conversation</string>
</resources>

View File

@@ -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<ConversationChannelWrapper> 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<ConversationChannelWrapper> 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.
*
* <p>A valid {@code conversation} must:
* <ul>
* <li>Have a non-null {@link ShortcutInfo}
* <li>Have an associated label in the {@link ShortcutInfo}
* </ul>
* </li>
*/
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();
}
}

View File

@@ -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));
}
}