mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 15:26:58 +00:00
Initial implementation: Broadcasts on app launch.
Look for com.android.launcher3.action.LAUNCH to be sent when an icon is clicked in Launcher. (Restricted to com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS which is a signature permission right now. This is specifically tracking apps launched via shortcut icon; any other method of launching apps (notifications, recents, internal navigation, etc.) is outside of Launcher's purview and hence not broadcast. The broadcast currently includes, in the "intent" extra, the Uri flattening of the specific shortcut clicked. The file /data/data/<pkg>/files/launches.log contains a binary log of all such launches, including additional info like screen# that should probably be in the broadcast too. This info is summarized in .../stats.log, which encodes a simple histogram of app launches since basically forever. This should probably be done over a sliding window, which will require more processing on startup. Bug: 10031590 Change-Id: Ifc5921d5dc20701c67678cbfdc89b03cacd62028
This commit is contained in:
@@ -50,6 +50,11 @@
|
||||
android:label="@string/permlab_write_settings"
|
||||
android:description="@string/permdesc_write_settings"/>
|
||||
|
||||
<permission
|
||||
android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"
|
||||
android:protectionLevel="signature"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
|
||||
@@ -60,6 +65,7 @@
|
||||
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
|
||||
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
|
||||
<uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
|
||||
<uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
|
||||
|
||||
<application
|
||||
android:name="com.android.launcher3.LauncherApplication"
|
||||
|
||||
@@ -345,6 +345,8 @@ public class Launcher extends Activity
|
||||
int cellY;
|
||||
}
|
||||
|
||||
private Stats mStats;
|
||||
|
||||
private static boolean isPropertyEnabled(String propertyName) {
|
||||
return Log.isLoggable(propertyName, Log.VERBOSE);
|
||||
}
|
||||
@@ -378,6 +380,8 @@ public class Launcher extends Activity
|
||||
mDragController = new DragController(this);
|
||||
mInflater = getLayoutInflater();
|
||||
|
||||
mStats = new Stats(this);
|
||||
|
||||
mAppWidgetManager = AppWidgetManager.getInstance(this);
|
||||
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
|
||||
mAppWidgetHost.startListening();
|
||||
@@ -2086,7 +2090,8 @@ public class Launcher extends Activity
|
||||
Object tag = v.getTag();
|
||||
if (tag instanceof ShortcutInfo) {
|
||||
// Open shortcut
|
||||
final Intent intent = ((ShortcutInfo) tag).intent;
|
||||
final ShortcutInfo shortcut = (ShortcutInfo) tag;
|
||||
final Intent intent = shortcut.intent;
|
||||
|
||||
// Check for special shortcuts
|
||||
if (intent.getComponent() != null) {
|
||||
@@ -2112,6 +2117,8 @@ public class Launcher extends Activity
|
||||
|
||||
boolean success = startActivitySafely(v, intent, tag);
|
||||
|
||||
mStats.recordLaunch(intent, shortcut);
|
||||
|
||||
if (success && v instanceof BubbleTextView) {
|
||||
mWaitingForResume = (BubbleTextView) v;
|
||||
mWaitingForResume.setStayPressed(true);
|
||||
|
||||
202
src/com/android/launcher3/Stats.java
Normal file
202
src/com/android/launcher3/Stats.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.launcher3;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Stats {
|
||||
private static final boolean DEBUG_BROADCASTS = false;
|
||||
private static final String TAG = "Launcher3/Stats";
|
||||
|
||||
private static final boolean LOCAL_LAUNCH_LOG = true;
|
||||
|
||||
public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
|
||||
public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
|
||||
public static final String EXTRA_INTENT = "intent";
|
||||
public static final String EXTRA_CONTAINER = "container";
|
||||
public static final String EXTRA_SCREEN = "screen";
|
||||
public static final String EXTRA_CELLX = "cellX";
|
||||
public static final String EXTRA_CELLY = "cellY";
|
||||
|
||||
private static final String LOG_FILE_NAME = "launches.log";
|
||||
private static final int LOG_VERSION = 1;
|
||||
private static final int LOG_TAG_VERSION = 0x1;
|
||||
private static final int LOG_TAG_LAUNCH = 0x1000;
|
||||
|
||||
private static final String STATS_FILE_NAME = "stats.log";
|
||||
private static final int STATS_VERSION = 1;
|
||||
private static final int INITIAL_STATS_SIZE = 100;
|
||||
|
||||
// TODO: delayed/batched writes
|
||||
private static final boolean FLUSH_IMMEDIATELY = true;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
DataOutputStream mLog;
|
||||
|
||||
ArrayList<String> mIntents;
|
||||
ArrayList<Integer> mHistogram;
|
||||
|
||||
public Stats(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
|
||||
loadStats();
|
||||
|
||||
if (LOCAL_LAUNCH_LOG) {
|
||||
try {
|
||||
mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND));
|
||||
mLog.writeInt(LOG_TAG_VERSION);
|
||||
mLog.writeInt(LOG_VERSION);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "unable to create stats log: " + e);
|
||||
mLog = null;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "unable to write to stats log: " + e);
|
||||
mLog = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG_BROADCASTS) {
|
||||
launcher.registerReceiver(
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
|
||||
+ intent.getStringExtra(EXTRA_INTENT));
|
||||
}
|
||||
},
|
||||
new IntentFilter(ACTION_LAUNCH),
|
||||
PERM_LAUNCH,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementLaunch(String intentStr) {
|
||||
int pos = mIntents.indexOf(intentStr);
|
||||
if (pos < 0) {
|
||||
mIntents.add(intentStr);
|
||||
mHistogram.add(1);
|
||||
} else {
|
||||
mHistogram.set(pos, mHistogram.get(pos) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void recordLaunch(Intent intent, ShortcutInfo shortcut) {
|
||||
intent = new Intent(intent);
|
||||
intent.setSourceBounds(null);
|
||||
|
||||
final String flat = intent.toUri(0);
|
||||
|
||||
mLauncher.sendBroadcast(
|
||||
new Intent(ACTION_LAUNCH)
|
||||
.putExtra(EXTRA_INTENT, flat)
|
||||
.putExtra(EXTRA_CONTAINER, shortcut.container)
|
||||
.putExtra(EXTRA_SCREEN, shortcut.screenId)
|
||||
.putExtra(EXTRA_CELLX, shortcut.cellX)
|
||||
.putExtra(EXTRA_CELLY, shortcut.cellY),
|
||||
PERM_LAUNCH);
|
||||
|
||||
incrementLaunch(flat);
|
||||
|
||||
if (FLUSH_IMMEDIATELY) {
|
||||
saveStats();
|
||||
}
|
||||
|
||||
if (LOCAL_LAUNCH_LOG && mLog != null) {
|
||||
try {
|
||||
mLog.writeInt(LOG_TAG_LAUNCH);
|
||||
mLog.writeLong(System.currentTimeMillis());
|
||||
mLog.writeShort((short) shortcut.container);
|
||||
mLog.writeShort((short) shortcut.screenId);
|
||||
mLog.writeShort((short) shortcut.cellX);
|
||||
mLog.writeShort((short) shortcut.cellY);
|
||||
mLog.writeUTF(flat);
|
||||
if (FLUSH_IMMEDIATELY) {
|
||||
mLog.flush(); // TODO: delayed writes
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveStats() {
|
||||
DataOutputStream stats = null;
|
||||
try {
|
||||
stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE));
|
||||
stats.writeInt(STATS_VERSION);
|
||||
final int N = mHistogram.size();
|
||||
stats.writeInt(N);
|
||||
for (int i=0; i<N; i++) {
|
||||
stats.writeUTF(mIntents.get(i));
|
||||
stats.writeInt(mHistogram.get(i));
|
||||
}
|
||||
stats.close();
|
||||
stats = null;
|
||||
mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp")
|
||||
.renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME));
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "unable to create stats data: " + e);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "unable to write to stats data: " + e);
|
||||
} finally {
|
||||
if (stats != null) {
|
||||
try {
|
||||
stats.close();
|
||||
} catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStats() {
|
||||
mIntents = new ArrayList<String>(INITIAL_STATS_SIZE);
|
||||
mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE);
|
||||
DataInputStream stats = null;
|
||||
try {
|
||||
stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME));
|
||||
final int version = stats.readInt();
|
||||
if (version == STATS_VERSION) {
|
||||
final int N = stats.readInt();
|
||||
for (int i=0; i<N; i++) {
|
||||
final String pkg = stats.readUTF();
|
||||
final int count = stats.readInt();
|
||||
mIntents.add(pkg);
|
||||
mHistogram.add(count);
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// not a problem
|
||||
} catch (IOException e) {
|
||||
// more of a problem
|
||||
|
||||
} finally {
|
||||
if (stats != null) {
|
||||
try {
|
||||
stats.close();
|
||||
} catch (IOException e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user