Merge "Migrate UsageStats to Spa"
This commit is contained in:
@@ -2911,15 +2911,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="UsageStatsActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/usage_stats_label">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="Settings$PowerUsageSummaryActivity"
|
android:name="Settings$PowerUsageSummaryActivity"
|
||||||
android:label="@string/power_usage_summary_title"
|
android:label="@string/power_usage_summary_title"
|
||||||
@@ -4584,10 +4575,20 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity android:name=".spa.SpaActivity" android:exported="false"/>
|
||||||
android:name="com.android.settings.spa.SpaActivity"
|
<activity android:name=".spa.SpaBridgeActivity" android:exported="false"/>
|
||||||
android:exported="false">
|
|
||||||
</activity>
|
<activity-alias android:name="UsageStatsActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/testing_usage_stats"
|
||||||
|
android:targetActivity=".spa.SpaBridgeActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data android:name="com.android.settings.spa.DESTINATION"
|
||||||
|
android:value="UsageStats"/>
|
||||||
|
</activity-alias>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.android.settings.spa.SpaDebugActivity"
|
android:name="com.android.settings.spa.SpaDebugActivity"
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:text="@string/display_order_text"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/typeSpinner"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:entries="@array/usage_stats_display_order_types" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" >
|
|
||||||
<TextView
|
|
||||||
android:text="@string/app_name_label"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:paddingEnd="6dip"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
<TextView
|
|
||||||
android:text="@string/last_time_used_label"
|
|
||||||
android:paddingEnd="6dip"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
<TextView
|
|
||||||
android:text="@string/usage_time_label"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
</LinearLayout>
|
|
||||||
<ListView android:id="@+id/pkg_list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:drawSelectorOnTop="false" />
|
|
||||||
</LinearLayout>
|
|
@@ -1,50 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
/*
|
|
||||||
** Copyright 2008, 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:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight">
|
|
||||||
|
|
||||||
<TextView android:id="@+id/package_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:paddingEnd="6dip"
|
|
||||||
android:paddingStart="12dip"
|
|
||||||
android:maxLines="1" />
|
|
||||||
|
|
||||||
<TextView android:id="@+id/last_time_used"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:paddingEnd="6dip"
|
|
||||||
android:paddingStart="12dip"
|
|
||||||
android:maxLines="1" />
|
|
||||||
|
|
||||||
<TextView android:id="@+id/usage_time"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
android:paddingEnd="6dip"
|
|
||||||
android:paddingStart="12dip"
|
|
||||||
android:maxLines="1" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
@@ -261,13 +261,6 @@
|
|||||||
<item>Excellent</item>
|
<item>Excellent</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<!-- Display options for UsageStats class -->
|
|
||||||
<string-array name="usage_stats_display_order_types">
|
|
||||||
<item>Usage time</item>
|
|
||||||
<item>Last time used</item>
|
|
||||||
<item>App name</item>
|
|
||||||
</string-array>
|
|
||||||
|
|
||||||
<!-- Phase 2 options for PEAP -->
|
<!-- Phase 2 options for PEAP -->
|
||||||
<string-array name="wifi_peap_phase2_entries">
|
<string-array name="wifi_peap_phase2_entries">
|
||||||
<item>MSCHAPV2</item>
|
<item>MSCHAPV2</item>
|
||||||
|
@@ -4555,15 +4555,14 @@
|
|||||||
<!-- Text for checkbox that pops up when an app requests permission to bind a widget [CHAR LIMIT=NONE] -->
|
<!-- Text for checkbox that pops up when an app requests permission to bind a widget [CHAR LIMIT=NONE] -->
|
||||||
<string name="allow_bind_app_widget_activity_always_allow_bind">Always allow <xliff:g id="widget_host_name">%1$s</xliff:g> to create widgets and access their data</string>
|
<string name="allow_bind_app_widget_activity_always_allow_bind">Always allow <xliff:g id="widget_host_name">%1$s</xliff:g> to create widgets and access their data</string>
|
||||||
|
|
||||||
<!-- XXX remove? Strings used for displaying usage statistics -->
|
|
||||||
<string name="usage_stats_label">Usage statistics</string>
|
|
||||||
|
|
||||||
<!-- In the Testing screen. The item title of the activity that shows usage statistics. -->
|
<!-- In the Testing screen. The item title of the activity that shows usage statistics. -->
|
||||||
<string name="testing_usage_stats">Usage statistics</string>
|
<string name="testing_usage_stats">Usage statistics</string>
|
||||||
<!-- label for text to indicate sort options -->
|
<!-- Spinner label to indicate sort by usage time. [CHAR LIMIT=30] -->
|
||||||
<string name="display_order_text">Sort by:</string>
|
<string name="usage_stats_sort_by_usage_time">Sort by usage time</string>
|
||||||
<!-- label for application name -->
|
<!-- Spinner label to indicate sort by last time used. [CHAR LIMIT=30] -->
|
||||||
<string name="app_name_label">App</string>
|
<string name="usage_stats_sort_by_last_time_used">Sort by last time used</string>
|
||||||
|
<!-- Spinner label to indicate sort by app name. [CHAR LIMIT=30] -->
|
||||||
|
<string name="usage_stats_sort_by_app_name">Sort by app name</string>
|
||||||
<!-- label for last time used -->
|
<!-- label for last time used -->
|
||||||
<string name="last_time_used_label">Last time used</string>
|
<string name="last_time_used_label">Last time used</string>
|
||||||
<!-- label for usage time -->
|
<!-- label for usage time -->
|
||||||
|
@@ -1,255 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (C) 2007 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.settings;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.usage.UsageStats;
|
|
||||||
import android.app.usage.UsageStatsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activity to display package usage statistics.
|
|
||||||
*/
|
|
||||||
public class UsageStatsActivity extends Activity implements OnItemSelectedListener {
|
|
||||||
private static final String TAG = "UsageStatsActivity";
|
|
||||||
private static final boolean localLOGV = false;
|
|
||||||
private UsageStatsManager mUsageStatsManager;
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
private UsageStatsAdapter mAdapter;
|
|
||||||
private PackageManager mPm;
|
|
||||||
|
|
||||||
public static class AppNameComparator implements Comparator<UsageStats> {
|
|
||||||
private Map<String, String> mAppLabelList;
|
|
||||||
|
|
||||||
AppNameComparator(Map<String, String> appList) {
|
|
||||||
mAppLabelList = appList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int compare(UsageStats a, UsageStats b) {
|
|
||||||
String alabel = mAppLabelList.get(a.getPackageName());
|
|
||||||
String blabel = mAppLabelList.get(b.getPackageName());
|
|
||||||
return alabel.compareTo(blabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LastTimeUsedComparator implements Comparator<UsageStats> {
|
|
||||||
@Override
|
|
||||||
public final int compare(UsageStats a, UsageStats b) {
|
|
||||||
// return by descending order
|
|
||||||
return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UsageTimeComparator implements Comparator<UsageStats> {
|
|
||||||
@Override
|
|
||||||
public final int compare(UsageStats a, UsageStats b) {
|
|
||||||
return Long.compare(b.getTotalTimeInForeground(), a.getTotalTimeInForeground());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// View Holder used when displaying views
|
|
||||||
static class AppViewHolder {
|
|
||||||
TextView pkgName;
|
|
||||||
TextView lastTimeUsed;
|
|
||||||
TextView usageTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
class UsageStatsAdapter extends BaseAdapter {
|
|
||||||
// Constants defining order for display order
|
|
||||||
private static final int _DISPLAY_ORDER_USAGE_TIME = 0;
|
|
||||||
private static final int _DISPLAY_ORDER_LAST_TIME_USED = 1;
|
|
||||||
private static final int _DISPLAY_ORDER_APP_NAME = 2;
|
|
||||||
|
|
||||||
private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME;
|
|
||||||
private LastTimeUsedComparator mLastTimeUsedComparator = new LastTimeUsedComparator();
|
|
||||||
private UsageTimeComparator mUsageTimeComparator = new UsageTimeComparator();
|
|
||||||
private AppNameComparator mAppLabelComparator;
|
|
||||||
private final ArrayMap<String, String> mAppLabelMap = new ArrayMap<>();
|
|
||||||
private final ArrayList<UsageStats> mPackageStats = new ArrayList<>();
|
|
||||||
|
|
||||||
UsageStatsAdapter() {
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.add(Calendar.DAY_OF_YEAR, -5);
|
|
||||||
|
|
||||||
final List<UsageStats> stats =
|
|
||||||
mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
|
|
||||||
cal.getTimeInMillis(), System.currentTimeMillis());
|
|
||||||
if (stats == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayMap<String, UsageStats> map = new ArrayMap<>();
|
|
||||||
final int statCount = stats.size();
|
|
||||||
for (int i = 0; i < statCount; i++) {
|
|
||||||
final android.app.usage.UsageStats pkgStats = stats.get(i);
|
|
||||||
|
|
||||||
// load application labels for each application
|
|
||||||
try {
|
|
||||||
ApplicationInfo appInfo = mPm.getApplicationInfo(pkgStats.getPackageName(), 0);
|
|
||||||
String label = appInfo.loadLabel(mPm).toString();
|
|
||||||
mAppLabelMap.put(pkgStats.getPackageName(), label);
|
|
||||||
|
|
||||||
UsageStats existingStats =
|
|
||||||
map.get(pkgStats.getPackageName());
|
|
||||||
if (existingStats == null) {
|
|
||||||
map.put(pkgStats.getPackageName(), pkgStats);
|
|
||||||
} else {
|
|
||||||
existingStats.add(pkgStats);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
// This package may be gone.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mPackageStats.addAll(map.values());
|
|
||||||
|
|
||||||
// Sort list
|
|
||||||
mAppLabelComparator = new AppNameComparator(mAppLabelMap);
|
|
||||||
sortList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return mPackageStats.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
return mPackageStats.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
// A ViewHolder keeps references to children views to avoid unneccessary calls
|
|
||||||
// to findViewById() on each row.
|
|
||||||
AppViewHolder holder;
|
|
||||||
|
|
||||||
// When convertView is not null, we can reuse it directly, there is no need
|
|
||||||
// to reinflate it. We only inflate a new View when the convertView supplied
|
|
||||||
// by ListView is null.
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = mInflater.inflate(R.layout.usage_stats_item, null);
|
|
||||||
|
|
||||||
// Creates a ViewHolder and store references to the two children views
|
|
||||||
// we want to bind data to.
|
|
||||||
holder = new AppViewHolder();
|
|
||||||
holder.pkgName = (TextView) convertView.findViewById(R.id.package_name);
|
|
||||||
holder.lastTimeUsed = (TextView) convertView.findViewById(R.id.last_time_used);
|
|
||||||
holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time);
|
|
||||||
convertView.setTag(holder);
|
|
||||||
} else {
|
|
||||||
// Get the ViewHolder back to get fast access to the TextView
|
|
||||||
// and the ImageView.
|
|
||||||
holder = (AppViewHolder) convertView.getTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the data efficiently with the holder
|
|
||||||
UsageStats pkgStats = mPackageStats.get(position);
|
|
||||||
if (pkgStats != null) {
|
|
||||||
String label = mAppLabelMap.get(pkgStats.getPackageName());
|
|
||||||
holder.pkgName.setText(label);
|
|
||||||
holder.lastTimeUsed.setText(DateUtils.formatSameDayTime(pkgStats.getLastTimeUsed(),
|
|
||||||
System.currentTimeMillis(), DateFormat.MEDIUM, DateFormat.MEDIUM));
|
|
||||||
holder.usageTime.setText(
|
|
||||||
DateUtils.formatElapsedTime(pkgStats.getTotalTimeInForeground() / 1000));
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "No usage stats info for package:" + position);
|
|
||||||
}
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sortList(int sortOrder) {
|
|
||||||
if (mDisplayOrder == sortOrder) {
|
|
||||||
// do nothing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mDisplayOrder= sortOrder;
|
|
||||||
sortList();
|
|
||||||
}
|
|
||||||
private void sortList() {
|
|
||||||
if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) {
|
|
||||||
if (localLOGV) Log.i(TAG, "Sorting by usage time");
|
|
||||||
Collections.sort(mPackageStats, mUsageTimeComparator);
|
|
||||||
} else if (mDisplayOrder == _DISPLAY_ORDER_LAST_TIME_USED) {
|
|
||||||
if (localLOGV) Log.i(TAG, "Sorting by last time used");
|
|
||||||
Collections.sort(mPackageStats, mLastTimeUsedComparator);
|
|
||||||
} else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) {
|
|
||||||
if (localLOGV) Log.i(TAG, "Sorting by application name");
|
|
||||||
Collections.sort(mPackageStats, mAppLabelComparator);
|
|
||||||
}
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle icicle) {
|
|
||||||
super.onCreate(icicle);
|
|
||||||
setContentView(R.layout.usage_stats);
|
|
||||||
|
|
||||||
mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
|
|
||||||
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
mPm = getPackageManager();
|
|
||||||
|
|
||||||
Spinner typeSpinner = (Spinner) findViewById(R.id.typeSpinner);
|
|
||||||
typeSpinner.setOnItemSelectedListener(this);
|
|
||||||
|
|
||||||
ListView listView = (ListView) findViewById(R.id.pkg_list);
|
|
||||||
mAdapter = new UsageStatsAdapter();
|
|
||||||
listView.setAdapter(mAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
mAdapter.sortList(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
@@ -23,9 +23,9 @@ import com.android.settingslib.spa.framework.BrowseActivity
|
|||||||
class SpaActivity : BrowseActivity(SpaEnvironment.settingsPageProviders) {
|
class SpaActivity : BrowseActivity(SpaEnvironment.settingsPageProviders) {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun startSpaActivity(context: Context, startDestination: String) {
|
fun startSpaActivity(context: Context, destination: String) {
|
||||||
val intent = Intent(context, SpaActivity::class.java).apply {
|
val intent = Intent(context, SpaActivity::class.java).apply {
|
||||||
putExtra(KEY_DESTINATION, startDestination)
|
putExtra(KEY_DESTINATION, destination)
|
||||||
}
|
}
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
49
src/com/android/settings/spa/SpaBridgeActivity.kt
Executable file
49
src/com/android/settings/spa/SpaBridgeActivity.kt
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.settings.spa
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.PackageManager.ComponentInfoFlags
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity used as a bridge to [SpaActivity].
|
||||||
|
*
|
||||||
|
* Since [SpaActivity] is not exported, [SpaActivity] could not be the target activity of
|
||||||
|
* <activity-alias>, otherwise all its pages will be exported.
|
||||||
|
* So need this bridge activity to sit in the middle of <activity-alias> and [SpaActivity].
|
||||||
|
*/
|
||||||
|
class SpaBridgeActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
getDestination()?.let { destination ->
|
||||||
|
startSpaActivity(this, destination)
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDestination(): String? =
|
||||||
|
packageManager.getActivityInfo(
|
||||||
|
componentName, ComponentInfoFlags.of(PackageManager.GET_META_DATA.toLong())
|
||||||
|
).metaData.getString(META_DATA_KEY_DESTINATION)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val META_DATA_KEY_DESTINATION = "com.android.settings.spa.DESTINATION"
|
||||||
|
}
|
||||||
|
}
|
@@ -24,11 +24,12 @@ import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProv
|
|||||||
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
|
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
|
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
|
||||||
|
import com.android.settings.spa.development.UsageStatsPageProvider
|
||||||
import com.android.settings.spa.home.HomePageProvider
|
import com.android.settings.spa.home.HomePageProvider
|
||||||
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
|
|
||||||
import com.android.settingslib.spa.framework.common.SettingsPage
|
|
||||||
import com.android.settings.spa.notification.AppListNotificationsPageProvider
|
import com.android.settings.spa.notification.AppListNotificationsPageProvider
|
||||||
import com.android.settings.spa.notification.NotificationMainPageProvider
|
import com.android.settings.spa.notification.NotificationMainPageProvider
|
||||||
|
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
|
||||||
|
import com.android.settingslib.spa.framework.common.SettingsPage
|
||||||
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
|
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
|
||||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate
|
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ object SpaEnvironment {
|
|||||||
SpecialAppAccessPageProvider,
|
SpecialAppAccessPageProvider,
|
||||||
NotificationMainPageProvider,
|
NotificationMainPageProvider,
|
||||||
AppListNotificationsPageProvider,
|
AppListNotificationsPageProvider,
|
||||||
|
UsageStatsPageProvider,
|
||||||
) + togglePermissionAppListTemplate.createPageProviders(),
|
) + togglePermissionAppListTemplate.createPageProviders(),
|
||||||
rootPages = listOf(
|
rootPages = listOf(
|
||||||
SettingsPage.create(HomePageProvider.name),
|
SettingsPage.create(HomePageProvider.name),
|
||||||
|
52
src/com/android/settings/spa/development/UsageStats.kt
Normal file
52
src/com/android/settings/spa/development/UsageStats.kt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.settings.spa.development
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||||
|
import com.android.settingslib.spa.framework.compose.navigator
|
||||||
|
import com.android.settingslib.spa.framework.compose.rememberContext
|
||||||
|
import com.android.settingslib.spa.widget.preference.Preference
|
||||||
|
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||||
|
import com.android.settingslib.spaprivileged.template.app.AppListItem
|
||||||
|
import com.android.settingslib.spaprivileged.template.app.AppListPage
|
||||||
|
|
||||||
|
object UsageStatsPageProvider : SettingsPageProvider {
|
||||||
|
override val name = "UsageStats"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Page(arguments: Bundle?) {
|
||||||
|
AppListPage(
|
||||||
|
title = stringResource(R.string.testing_usage_stats),
|
||||||
|
listModel = rememberContext(::UsageStatsListModel),
|
||||||
|
primaryUserOnly = true,
|
||||||
|
) { itemModel ->
|
||||||
|
AppListItem(itemModel) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EntryItem() {
|
||||||
|
Preference(object : PreferenceModel {
|
||||||
|
override val title = stringResource(R.string.testing_usage_stats)
|
||||||
|
override val onClick = navigator(name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
101
src/com/android/settings/spa/development/UsageStatsListModel.kt
Normal file
101
src/com/android/settings/spa/development/UsageStatsListModel.kt
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.settings.spa.development
|
||||||
|
|
||||||
|
import android.app.usage.UsageStats
|
||||||
|
import android.app.usage.UsageStatsManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.text.format.DateUtils
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.spa.development.UsageStatsListModel.SpinnerItem.Companion.toSpinnerItem
|
||||||
|
import com.android.settingslib.spa.framework.compose.stateOf
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppEntry
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppListModel
|
||||||
|
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
data class UsageStatsAppRecord(
|
||||||
|
override val app: ApplicationInfo,
|
||||||
|
val usageStats: UsageStats?,
|
||||||
|
) : AppRecord
|
||||||
|
|
||||||
|
class UsageStatsListModel(private val context: Context) : AppListModel<UsageStatsAppRecord> {
|
||||||
|
private val usageStatsManager =
|
||||||
|
context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
|
||||||
|
private val now = System.currentTimeMillis()
|
||||||
|
|
||||||
|
override fun transform(
|
||||||
|
userIdFlow: Flow<Int>,
|
||||||
|
appListFlow: Flow<List<ApplicationInfo>>,
|
||||||
|
) = userIdFlow.map { getUsageStats() }
|
||||||
|
.combine(appListFlow) { usageStatsMap, appList ->
|
||||||
|
appList.map { app -> UsageStatsAppRecord(app, usageStatsMap[app.packageName]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSpinnerOptions() = SpinnerItem.values().map {
|
||||||
|
context.getString(it.stringResId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun filter(
|
||||||
|
userIdFlow: Flow<Int>,
|
||||||
|
option: Int,
|
||||||
|
recordListFlow: Flow<List<UsageStatsAppRecord>>,
|
||||||
|
) = recordListFlow.map { recordList ->
|
||||||
|
recordList.filter { it.usageStats != null }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getComparator(option: Int) = when (option.toSpinnerItem()) {
|
||||||
|
SpinnerItem.UsageTime -> compareByDescending { it.record.usageStats?.totalTimeInForeground }
|
||||||
|
SpinnerItem.LastTimeUsed -> compareByDescending { it.record.usageStats?.lastTimeUsed }
|
||||||
|
else -> compareBy<AppEntry<UsageStatsAppRecord>> { 0 }
|
||||||
|
}.then(super.getComparator(option))
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun getSummary(option: Int, record: UsageStatsAppRecord): State<String>? {
|
||||||
|
val usageStats = record.usageStats ?: return null
|
||||||
|
val lastTimeUsed = DateUtils.formatSameDayTime(
|
||||||
|
usageStats.lastTimeUsed, now, DateFormat.MEDIUM, DateFormat.MEDIUM)
|
||||||
|
val lastTimeUsedLine = "${context.getString(R.string.last_time_used_label)}: $lastTimeUsed"
|
||||||
|
val usageTime = DateUtils.formatElapsedTime(usageStats.totalTimeInForeground / 1000)
|
||||||
|
val usageTimeLine = "${context.getString(R.string.usage_time_label)}: $usageTime"
|
||||||
|
return stateOf("$lastTimeUsedLine\n$usageTimeLine")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUsageStats(): Map<String, UsageStats> {
|
||||||
|
val startTime = now - TimeUnit.DAYS.toMillis(5)
|
||||||
|
|
||||||
|
return usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, now)
|
||||||
|
.groupingBy { it.packageName }.reduce { _, a, b -> a.add(b); a }
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class SpinnerItem(val stringResId: Int) {
|
||||||
|
UsageTime(R.string.usage_stats_sort_by_usage_time),
|
||||||
|
LastTimeUsed(R.string.usage_stats_sort_by_last_time_used),
|
||||||
|
AppName(R.string.usage_stats_sort_by_app_name);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun Int.toSpinnerItem(): SpinnerItem = values()[this]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user