471 lines
21 KiB
Java
471 lines
21 KiB
Java
package com.squareup.leakcanary.internal;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.app.ActionBar;
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.os.AsyncTask;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.text.format.DateUtils;
|
|
import android.text.format.Formatter;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.AdapterView;
|
|
import android.widget.BaseAdapter;
|
|
import android.widget.Button;
|
|
import android.widget.ListAdapter;
|
|
import android.widget.ListView;
|
|
import android.widget.TextView;
|
|
import androidx.core.content.FileProvider;
|
|
import com.squareup.leakcanary.AnalysisResult;
|
|
import com.squareup.leakcanary.AnalyzedHeap;
|
|
import com.squareup.leakcanary.BuildConfig;
|
|
import com.squareup.leakcanary.CanaryLog;
|
|
import com.squareup.leakcanary.HeapDump;
|
|
import com.squareup.leakcanary.LeakCanary;
|
|
import com.squareup.leakcanary.LeakDirectoryProvider;
|
|
import com.squareup.leakcanary.R;
|
|
import java.io.File;
|
|
import java.io.FilenameFilter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.concurrent.Executor;
|
|
|
|
/* loaded from: classes.dex */
|
|
public final class DisplayLeakActivity extends Activity {
|
|
private static final String SHOW_LEAK_EXTRA = "show_latest";
|
|
private Button actionButton;
|
|
private TextView failureView;
|
|
List<AnalyzedHeap> leaks;
|
|
private ListView listView;
|
|
String visibleLeakRefKey;
|
|
|
|
class LeakListAdapter extends BaseAdapter {
|
|
LeakListAdapter() {
|
|
}
|
|
|
|
@Override // android.widget.Adapter
|
|
public int getCount() {
|
|
return DisplayLeakActivity.this.leaks.size();
|
|
}
|
|
|
|
@Override // android.widget.Adapter
|
|
public long getItemId(int i) {
|
|
return i;
|
|
}
|
|
|
|
@Override // android.widget.Adapter
|
|
public View getView(int i, View view, ViewGroup viewGroup) {
|
|
String str;
|
|
String string;
|
|
if (view == null) {
|
|
view = LayoutInflater.from(DisplayLeakActivity.this).inflate(R.layout.leak_canary_leak_row, viewGroup, false);
|
|
}
|
|
TextView textView = (TextView) view.findViewById(R.id.leak_canary_row_text);
|
|
TextView textView2 = (TextView) view.findViewById(R.id.leak_canary_row_time);
|
|
AnalyzedHeap item = getItem(i);
|
|
String str2 = (DisplayLeakActivity.this.leaks.size() - i) + ". ";
|
|
AnalysisResult analysisResult = item.result;
|
|
if (analysisResult.failure != null) {
|
|
str = str2 + item.result.failure.getClass().getSimpleName() + " " + item.result.failure.getMessage();
|
|
} else {
|
|
String classSimpleName = DisplayLeakActivity.classSimpleName(analysisResult.className);
|
|
AnalysisResult analysisResult2 = item.result;
|
|
if (analysisResult2.leakFound) {
|
|
long j = analysisResult2.retainedHeapSize;
|
|
if (j == -1) {
|
|
string = DisplayLeakActivity.this.getString(R.string.leak_canary_class_has_leaked, new Object[]{classSimpleName});
|
|
} else {
|
|
string = DisplayLeakActivity.this.getString(R.string.leak_canary_class_has_leaked_retaining, new Object[]{classSimpleName, Formatter.formatShortFileSize(DisplayLeakActivity.this, j)});
|
|
}
|
|
if (item.result.excludedLeak) {
|
|
string = DisplayLeakActivity.this.getString(R.string.leak_canary_excluded_row, new Object[]{string});
|
|
}
|
|
str = str2 + string;
|
|
} else {
|
|
str = str2 + DisplayLeakActivity.this.getString(R.string.leak_canary_class_no_leak, new Object[]{classSimpleName});
|
|
}
|
|
}
|
|
textView.setText(str);
|
|
textView2.setText(DateUtils.formatDateTime(DisplayLeakActivity.this, item.selfLastModified, 17));
|
|
return view;
|
|
}
|
|
|
|
@Override // android.widget.Adapter
|
|
public AnalyzedHeap getItem(int i) {
|
|
return DisplayLeakActivity.this.leaks.get(i);
|
|
}
|
|
}
|
|
|
|
static class LoadLeaks implements Runnable {
|
|
DisplayLeakActivity activityOrNull;
|
|
private final LeakDirectoryProvider leakDirectoryProvider;
|
|
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
static final List<LoadLeaks> inFlight = new ArrayList();
|
|
static final Executor backgroundExecutor = LeakCanaryInternals.newSingleThreadExecutor("LoadLeaks");
|
|
|
|
LoadLeaks(DisplayLeakActivity displayLeakActivity, LeakDirectoryProvider leakDirectoryProvider) {
|
|
this.activityOrNull = displayLeakActivity;
|
|
this.leakDirectoryProvider = leakDirectoryProvider;
|
|
}
|
|
|
|
static void forgetActivity() {
|
|
Iterator<LoadLeaks> it = inFlight.iterator();
|
|
while (it.hasNext()) {
|
|
it.next().activityOrNull = null;
|
|
}
|
|
inFlight.clear();
|
|
}
|
|
|
|
static void load(DisplayLeakActivity displayLeakActivity, LeakDirectoryProvider leakDirectoryProvider) {
|
|
LoadLeaks loadLeaks = new LoadLeaks(displayLeakActivity, leakDirectoryProvider);
|
|
inFlight.add(loadLeaks);
|
|
backgroundExecutor.execute(loadLeaks);
|
|
}
|
|
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
final ArrayList arrayList = new ArrayList();
|
|
Iterator<File> it = this.leakDirectoryProvider.listFiles(new FilenameFilter() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.1
|
|
@Override // java.io.FilenameFilter
|
|
public boolean accept(File file, String str) {
|
|
return str.endsWith(".result");
|
|
}
|
|
}).iterator();
|
|
while (it.hasNext()) {
|
|
AnalyzedHeap load = AnalyzedHeap.load(it.next());
|
|
if (load != null) {
|
|
arrayList.add(load);
|
|
}
|
|
}
|
|
Collections.sort(arrayList, new Comparator<AnalyzedHeap>() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.2
|
|
@Override // java.util.Comparator
|
|
public int compare(AnalyzedHeap analyzedHeap, AnalyzedHeap analyzedHeap2) {
|
|
return Long.valueOf(analyzedHeap2.selfFile.lastModified()).compareTo(Long.valueOf(analyzedHeap.selfFile.lastModified()));
|
|
}
|
|
});
|
|
this.mainHandler.post(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.3
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
LoadLeaks.inFlight.remove(LoadLeaks.this);
|
|
DisplayLeakActivity displayLeakActivity = LoadLeaks.this.activityOrNull;
|
|
if (displayLeakActivity != null) {
|
|
displayLeakActivity.leaks = arrayList;
|
|
displayLeakActivity.updateUi();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
static String classSimpleName(String str) {
|
|
int lastIndexOf = str.lastIndexOf(46);
|
|
return lastIndexOf == -1 ? str : str.substring(lastIndexOf + 1);
|
|
}
|
|
|
|
public static PendingIntent createPendingIntent(Context context) {
|
|
return createPendingIntent(context, null);
|
|
}
|
|
|
|
private void setDisplayHomeAsUpEnabled(boolean z) {
|
|
ActionBar actionBar = getActionBar();
|
|
if (actionBar == null) {
|
|
return;
|
|
}
|
|
actionBar.setDisplayHomeAsUpEnabled(z);
|
|
}
|
|
|
|
/* JADX INFO: Access modifiers changed from: private */
|
|
public void startShareIntentChooser(Uri uri) {
|
|
Intent intent = new Intent("android.intent.action.SEND");
|
|
intent.setType("application/octet-stream");
|
|
intent.putExtra("android.intent.extra.STREAM", uri);
|
|
startActivity(Intent.createChooser(intent, getString(R.string.leak_canary_share_with)));
|
|
}
|
|
|
|
void deleteAllLeaks() {
|
|
final LeakDirectoryProvider leakDirectoryProvider = LeakCanaryInternals.getLeakDirectoryProvider(this);
|
|
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.5
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
leakDirectoryProvider.clearLeakDirectory();
|
|
}
|
|
});
|
|
this.leaks = Collections.emptyList();
|
|
updateUi();
|
|
}
|
|
|
|
void deleteVisibleLeak() {
|
|
final AnalyzedHeap visibleLeak = getVisibleLeak();
|
|
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.4
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
AnalyzedHeap analyzedHeap = visibleLeak;
|
|
File file = analyzedHeap.heapDump.heapDumpFile;
|
|
File file2 = analyzedHeap.selfFile;
|
|
if (!file2.delete()) {
|
|
CanaryLog.d("Could not delete result file %s", file2.getPath());
|
|
}
|
|
if (file.delete()) {
|
|
return;
|
|
}
|
|
CanaryLog.d("Could not delete heap dump file %s", file.getPath());
|
|
}
|
|
});
|
|
this.visibleLeakRefKey = null;
|
|
this.leaks.remove(visibleLeak);
|
|
updateUi();
|
|
}
|
|
|
|
AnalyzedHeap getVisibleLeak() {
|
|
List<AnalyzedHeap> list = this.leaks;
|
|
if (list == null) {
|
|
return null;
|
|
}
|
|
for (AnalyzedHeap analyzedHeap : list) {
|
|
if (analyzedHeap.heapDump.referenceKey.equals(this.visibleLeakRefKey)) {
|
|
return analyzedHeap;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
public void onBackPressed() {
|
|
if (this.visibleLeakRefKey == null) {
|
|
super.onBackPressed();
|
|
} else {
|
|
this.visibleLeakRefKey = null;
|
|
updateUi();
|
|
}
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
protected void onCreate(Bundle bundle) {
|
|
super.onCreate(bundle);
|
|
if (bundle != null) {
|
|
this.visibleLeakRefKey = bundle.getString("visibleLeakRefKey");
|
|
} else {
|
|
Intent intent = getIntent();
|
|
if (intent.hasExtra(SHOW_LEAK_EXTRA)) {
|
|
this.visibleLeakRefKey = intent.getStringExtra(SHOW_LEAK_EXTRA);
|
|
}
|
|
}
|
|
this.leaks = (List) getLastNonConfigurationInstance();
|
|
setContentView(R.layout.leak_canary_display_leak);
|
|
this.listView = (ListView) findViewById(R.id.leak_canary_display_leak_list);
|
|
this.failureView = (TextView) findViewById(R.id.leak_canary_display_leak_failure);
|
|
this.actionButton = (Button) findViewById(R.id.leak_canary_action);
|
|
updateUi();
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
public boolean onCreateOptionsMenu(Menu menu) {
|
|
AnalyzedHeap visibleLeak = getVisibleLeak();
|
|
if (visibleLeak == null) {
|
|
return false;
|
|
}
|
|
menu.add(R.string.leak_canary_share_leak).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.1
|
|
@Override // android.view.MenuItem.OnMenuItemClickListener
|
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
|
DisplayLeakActivity.this.shareLeak();
|
|
return true;
|
|
}
|
|
});
|
|
if (!visibleLeak.heapDumpFileExists) {
|
|
return true;
|
|
}
|
|
menu.add(R.string.leak_canary_share_heap_dump).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.2
|
|
@Override // android.view.MenuItem.OnMenuItemClickListener
|
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
|
DisplayLeakActivity.this.shareHeapDump();
|
|
return true;
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
LoadLeaks.forgetActivity();
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
|
if (menuItem.getItemId() != 16908332) {
|
|
return true;
|
|
}
|
|
this.visibleLeakRefKey = null;
|
|
updateUi();
|
|
return true;
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
protected void onResume() {
|
|
super.onResume();
|
|
LoadLeaks.load(this, LeakCanaryInternals.getLeakDirectoryProvider(this));
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
public Object onRetainNonConfigurationInstance() {
|
|
return this.leaks;
|
|
}
|
|
|
|
@Override // android.app.Activity
|
|
protected void onSaveInstanceState(Bundle bundle) {
|
|
super.onSaveInstanceState(bundle);
|
|
bundle.putString("visibleLeakRefKey", this.visibleLeakRefKey);
|
|
}
|
|
|
|
@Override // android.app.Activity, android.view.ContextThemeWrapper, android.content.ContextWrapper, android.content.Context
|
|
public void setTheme(int i) {
|
|
if (i != R.style.leak_canary_LeakCanary_Base) {
|
|
return;
|
|
}
|
|
super.setTheme(i);
|
|
}
|
|
|
|
@SuppressLint({"SetWorldReadable"})
|
|
void shareHeapDump() {
|
|
final File file = getVisibleLeak().heapDump.heapDumpFile;
|
|
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.3
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
file.setReadable(true, false);
|
|
final Uri uriForFile = FileProvider.getUriForFile(DisplayLeakActivity.this.getBaseContext(), "com.squareup.leakcanary.fileprovider." + DisplayLeakActivity.this.getApplication().getPackageName(), file);
|
|
DisplayLeakActivity.this.runOnUiThread(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.3.1
|
|
@Override // java.lang.Runnable
|
|
public void run() {
|
|
DisplayLeakActivity.this.startShareIntentChooser(uriForFile);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
void shareLeak() {
|
|
AnalyzedHeap visibleLeak = getVisibleLeak();
|
|
String leakInfo = LeakCanary.leakInfo(this, visibleLeak.heapDump, visibleLeak.result, true);
|
|
Intent intent = new Intent("android.intent.action.SEND");
|
|
intent.setType("text/plain");
|
|
intent.putExtra("android.intent.extra.TEXT", leakInfo);
|
|
startActivity(Intent.createChooser(intent, getString(R.string.leak_canary_share_with)));
|
|
}
|
|
|
|
void updateUi() {
|
|
String string;
|
|
List<AnalyzedHeap> list = this.leaks;
|
|
if (list == null) {
|
|
setTitle("Loading leaks...");
|
|
return;
|
|
}
|
|
if (list.isEmpty()) {
|
|
this.visibleLeakRefKey = null;
|
|
}
|
|
AnalyzedHeap visibleLeak = getVisibleLeak();
|
|
if (visibleLeak == null) {
|
|
this.visibleLeakRefKey = null;
|
|
}
|
|
ListAdapter adapter = this.listView.getAdapter();
|
|
this.listView.setVisibility(0);
|
|
this.failureView.setVisibility(8);
|
|
if (visibleLeak == null) {
|
|
if (adapter instanceof LeakListAdapter) {
|
|
((LeakListAdapter) adapter).notifyDataSetChanged();
|
|
} else {
|
|
this.listView.setAdapter((ListAdapter) new LeakListAdapter());
|
|
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.8
|
|
@Override // android.widget.AdapterView.OnItemClickListener
|
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
|
|
DisplayLeakActivity displayLeakActivity = DisplayLeakActivity.this;
|
|
displayLeakActivity.visibleLeakRefKey = displayLeakActivity.leaks.get(i).heapDump.referenceKey;
|
|
DisplayLeakActivity.this.updateUi();
|
|
}
|
|
});
|
|
invalidateOptionsMenu();
|
|
setTitle(getString(R.string.leak_canary_leak_list_title, new Object[]{getPackageName()}));
|
|
setDisplayHomeAsUpEnabled(false);
|
|
this.actionButton.setText(R.string.leak_canary_delete_all);
|
|
this.actionButton.setOnClickListener(new View.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.9
|
|
@Override // android.view.View.OnClickListener
|
|
public void onClick(View view) {
|
|
new AlertDialog.Builder(DisplayLeakActivity.this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.leak_canary_delete_all).setMessage(R.string.leak_canary_delete_all_leaks_title).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.9.1
|
|
@Override // android.content.DialogInterface.OnClickListener
|
|
public void onClick(DialogInterface dialogInterface, int i) {
|
|
DisplayLeakActivity.this.deleteAllLeaks();
|
|
}
|
|
}).setNegativeButton(android.R.string.cancel, (DialogInterface.OnClickListener) null).show();
|
|
}
|
|
});
|
|
}
|
|
this.actionButton.setVisibility(this.leaks.size() == 0 ? 8 : 0);
|
|
return;
|
|
}
|
|
AnalysisResult analysisResult = visibleLeak.result;
|
|
this.actionButton.setVisibility(0);
|
|
this.actionButton.setText(R.string.leak_canary_delete);
|
|
this.actionButton.setOnClickListener(new View.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.6
|
|
@Override // android.view.View.OnClickListener
|
|
public void onClick(View view) {
|
|
DisplayLeakActivity.this.deleteVisibleLeak();
|
|
}
|
|
});
|
|
invalidateOptionsMenu();
|
|
setDisplayHomeAsUpEnabled(true);
|
|
if (analysisResult.leakFound) {
|
|
final DisplayLeakAdapter displayLeakAdapter = new DisplayLeakAdapter(getResources());
|
|
this.listView.setAdapter((ListAdapter) displayLeakAdapter);
|
|
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.7
|
|
@Override // android.widget.AdapterView.OnItemClickListener
|
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
|
|
displayLeakAdapter.toggleRow(i);
|
|
}
|
|
});
|
|
HeapDump heapDump = visibleLeak.heapDump;
|
|
displayLeakAdapter.update(analysisResult.leakTrace, heapDump.referenceKey, heapDump.referenceName);
|
|
long j = analysisResult.retainedHeapSize;
|
|
if (j == -1) {
|
|
setTitle(getString(R.string.leak_canary_class_has_leaked, new Object[]{classSimpleName(analysisResult.className)}));
|
|
return;
|
|
} else {
|
|
setTitle(getString(R.string.leak_canary_class_has_leaked_retaining, new Object[]{classSimpleName(analysisResult.className), Formatter.formatShortFileSize(this, j)}));
|
|
return;
|
|
}
|
|
}
|
|
this.listView.setVisibility(8);
|
|
this.failureView.setVisibility(0);
|
|
this.listView.setAdapter((ListAdapter) null);
|
|
if (analysisResult.failure != null) {
|
|
setTitle(R.string.leak_canary_analysis_failed);
|
|
string = getString(R.string.leak_canary_failure_report) + BuildConfig.LIBRARY_VERSION + " " + BuildConfig.GIT_SHA + "\n" + Log.getStackTraceString(analysisResult.failure);
|
|
} else {
|
|
setTitle(getString(R.string.leak_canary_class_no_leak, new Object[]{classSimpleName(analysisResult.className)}));
|
|
string = getString(R.string.leak_canary_no_leak_details);
|
|
}
|
|
this.failureView.setText(string + "\n\n" + getString(R.string.leak_canary_download_dump, new Object[]{visibleLeak.heapDump.heapDumpFile.getAbsolutePath()}));
|
|
}
|
|
|
|
public static PendingIntent createPendingIntent(Context context, String str) {
|
|
LeakCanaryInternals.setEnabledBlocking(context, DisplayLeakActivity.class, true);
|
|
Intent intent = new Intent(context, (Class<?>) DisplayLeakActivity.class);
|
|
intent.putExtra(SHOW_LEAK_EXTRA, str);
|
|
intent.setFlags(335544320);
|
|
return PendingIntent.getActivity(context, 1, intent, 134217728);
|
|
}
|
|
}
|