Initial commit
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityResumed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
|
||||
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { // from class: com.squareup.leakcanary.internal.AndroidOFragmentRefWatcher.1
|
||||
@Override // android.app.FragmentManager.FragmentLifecycleCallbacks
|
||||
public void onFragmentDestroyed(FragmentManager fragmentManager, Fragment fragment) {
|
||||
AndroidOFragmentRefWatcher.this.refWatcher.watch(fragment);
|
||||
}
|
||||
|
||||
@Override // android.app.FragmentManager.FragmentLifecycleCallbacks
|
||||
public void onFragmentViewDestroyed(FragmentManager fragmentManager, Fragment fragment) {
|
||||
View view = fragment.getView();
|
||||
if (view != null) {
|
||||
AndroidOFragmentRefWatcher.this.refWatcher.watch(view);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final RefWatcher refWatcher;
|
||||
|
||||
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
|
||||
this.refWatcher = refWatcher;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.FragmentRefWatcher
|
||||
public void watchFragments(Activity activity) {
|
||||
activity.getFragmentManager().registerFragmentLifecycleCallbacks(this.fragmentLifecycleCallbacks, true);
|
||||
}
|
||||
}
|
@@ -0,0 +1,470 @@
|
||||
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);
|
||||
}
|
||||
}
|
250
sources/com/squareup/leakcanary/internal/DisplayLeakAdapter.java
Normal file
250
sources/com/squareup/leakcanary/internal/DisplayLeakAdapter.java
Normal file
@@ -0,0 +1,250 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
import com.squareup.leakcanary.Exclusion;
|
||||
import com.squareup.leakcanary.LeakReference;
|
||||
import com.squareup.leakcanary.LeakTrace;
|
||||
import com.squareup.leakcanary.LeakTraceElement;
|
||||
import com.squareup.leakcanary.R;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.squareup.leakcanary.internal.DisplayLeakConnectorView;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class DisplayLeakAdapter extends BaseAdapter {
|
||||
private static final int NORMAL_ROW = 1;
|
||||
private static final int TOP_ROW = 0;
|
||||
private final String classNameColorHexString;
|
||||
private final String extraColorHexString;
|
||||
private final String helpColorHexString;
|
||||
private final String leakColorHexString;
|
||||
private final String referenceColorHexString;
|
||||
private String referenceKey;
|
||||
private boolean[] opened = new boolean[0];
|
||||
private LeakTrace leakTrace = null;
|
||||
private String referenceName = "";
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.internal.DisplayLeakAdapter$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$leakcanary$Reachability = new int[Reachability.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.UNKNOWN.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.REACHABLE.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.UNREACHABLE.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisplayLeakAdapter(Resources resources) {
|
||||
this.classNameColorHexString = hexStringColor(resources, R.color.leak_canary_class_name);
|
||||
this.leakColorHexString = hexStringColor(resources, R.color.leak_canary_leak);
|
||||
this.referenceColorHexString = hexStringColor(resources, R.color.leak_canary_reference);
|
||||
this.extraColorHexString = hexStringColor(resources, R.color.leak_canary_extra);
|
||||
this.helpColorHexString = hexStringColor(resources, R.color.leak_canary_help);
|
||||
}
|
||||
|
||||
private int elementIndex(int i) {
|
||||
return i - 2;
|
||||
}
|
||||
|
||||
private static <T extends View> T findById(View view, int i) {
|
||||
return (T) view.findViewById(i);
|
||||
}
|
||||
|
||||
private DisplayLeakConnectorView.Type getConnectorType(int i) {
|
||||
if (i == 1) {
|
||||
return DisplayLeakConnectorView.Type.HELP;
|
||||
}
|
||||
if (i == 2) {
|
||||
return this.leakTrace.expectedReachability.size() == 1 ? DisplayLeakConnectorView.Type.START_LAST_REACHABLE : this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE ? DisplayLeakConnectorView.Type.START_LAST_REACHABLE : DisplayLeakConnectorView.Type.START;
|
||||
}
|
||||
if (i == getCount() - 1) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i - 1)) != Reachability.UNREACHABLE ? DisplayLeakConnectorView.Type.END_FIRST_UNREACHABLE : DisplayLeakConnectorView.Type.END;
|
||||
}
|
||||
Reachability reachability = this.leakTrace.expectedReachability.get(elementIndex(i));
|
||||
int i2 = AnonymousClass1.$SwitchMap$com$squareup$leakcanary$Reachability[reachability.ordinal()];
|
||||
if (i2 == 1) {
|
||||
return DisplayLeakConnectorView.Type.NODE_UNKNOWN;
|
||||
}
|
||||
if (i2 == 2) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE ? DisplayLeakConnectorView.Type.NODE_LAST_REACHABLE : DisplayLeakConnectorView.Type.NODE_REACHABLE;
|
||||
}
|
||||
if (i2 == 3) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i - 1)) != Reachability.UNREACHABLE ? DisplayLeakConnectorView.Type.NODE_FIRST_UNREACHABLE : DisplayLeakConnectorView.Type.NODE_UNREACHABLE;
|
||||
}
|
||||
throw new IllegalStateException("Unknown value: " + reachability);
|
||||
}
|
||||
|
||||
private static String hexStringColor(Resources resources, int i) {
|
||||
return String.format("#%06X", Integer.valueOf(resources.getColor(i) & 16777215));
|
||||
}
|
||||
|
||||
private Spanned htmlDetails(boolean z, LeakTraceElement leakTraceElement) {
|
||||
String str;
|
||||
if (leakTraceElement.extra != null) {
|
||||
str = " <font color='" + this.extraColorHexString + "'>" + leakTraceElement.extra + "</font>";
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
Exclusion exclusion = leakTraceElement.exclusion;
|
||||
if (exclusion != null) {
|
||||
String str2 = str + "<br/><br/>Excluded by rule";
|
||||
if (exclusion.name != null) {
|
||||
str2 = str2 + " <font color='#ffffff'>" + exclusion.name + "</font>";
|
||||
}
|
||||
str = str2 + " matching <font color='#f3cf83'>" + exclusion.matching + "</font>";
|
||||
if (exclusion.reason != null) {
|
||||
str = str + " because <font color='#f3cf83'>" + exclusion.reason + "</font>";
|
||||
}
|
||||
}
|
||||
String str3 = str + "<br><font color='" + this.extraColorHexString + "'>" + leakTraceElement.toDetailedString().replace("\n", "<br>") + "</font>";
|
||||
if (z && !this.referenceName.equals("")) {
|
||||
str3 = str3 + " <font color='" + this.extraColorHexString + "'>" + this.referenceName + "</font>";
|
||||
}
|
||||
return Html.fromHtml(str3);
|
||||
}
|
||||
|
||||
private Spanned htmlTitle(LeakTraceElement leakTraceElement, boolean z, Resources resources) {
|
||||
String str;
|
||||
String str2;
|
||||
String str3 = "<font color='" + this.classNameColorHexString + "'>" + leakTraceElement.getSimpleClassName().replace("[]", "[ ]") + "</font>";
|
||||
LeakReference leakReference = leakTraceElement.reference;
|
||||
if (leakReference != null) {
|
||||
String replaceAll = leakReference.getDisplayName().replaceAll("<", "<").replaceAll(">", ">");
|
||||
if (z) {
|
||||
str2 = "<u><font color='" + this.leakColorHexString + "'>" + replaceAll + "</font></u>";
|
||||
} else {
|
||||
str2 = "<font color='" + this.referenceColorHexString + "'>" + replaceAll + "</font>";
|
||||
}
|
||||
if (leakTraceElement.reference.type == LeakTraceElement.Type.STATIC_FIELD) {
|
||||
str2 = "<i>" + str2 + "</i>";
|
||||
}
|
||||
String str4 = str3 + "." + str2;
|
||||
if (z) {
|
||||
str4 = "<b>" + str4 + "</b>";
|
||||
}
|
||||
str = "" + str4;
|
||||
} else {
|
||||
str = "" + str3;
|
||||
}
|
||||
if (leakTraceElement.exclusion != null) {
|
||||
str = str + " (excluded)";
|
||||
}
|
||||
SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) Html.fromHtml(str);
|
||||
if (z) {
|
||||
SquigglySpan.replaceUnderlineSpans(spannableStringBuilder, resources);
|
||||
}
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public int getCount() {
|
||||
LeakTrace leakTrace = this.leakTrace;
|
||||
if (leakTrace == null) {
|
||||
return 2;
|
||||
}
|
||||
return leakTrace.elements.size() + 2;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override // android.widget.BaseAdapter, android.widget.Adapter
|
||||
public int getItemViewType(int i) {
|
||||
return i == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
Context context = viewGroup.getContext();
|
||||
boolean z = false;
|
||||
if (getItemViewType(i) == 0) {
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.leak_canary_ref_top_row, viewGroup, false);
|
||||
}
|
||||
((TextView) findById(view, R.id.leak_canary_row_text)).setText(context.getPackageName());
|
||||
} else {
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.leak_canary_ref_row, viewGroup, false);
|
||||
}
|
||||
TextView textView = (TextView) findById(view, R.id.leak_canary_row_title);
|
||||
TextView textView2 = (TextView) findById(view, R.id.leak_canary_row_details);
|
||||
DisplayLeakConnectorView displayLeakConnectorView = (DisplayLeakConnectorView) findById(view, R.id.leak_canary_row_connector);
|
||||
MoreDetailsView moreDetailsView = (MoreDetailsView) findById(view, R.id.leak_canary_row_more);
|
||||
displayLeakConnectorView.setType(getConnectorType(i));
|
||||
moreDetailsView.setOpened(this.opened[i]);
|
||||
if (this.opened[i]) {
|
||||
textView2.setVisibility(0);
|
||||
} else {
|
||||
textView2.setVisibility(8);
|
||||
}
|
||||
Resources resources = view.getResources();
|
||||
if (i == 1) {
|
||||
textView.setText(Html.fromHtml("<font color='" + this.helpColorHexString + "'><b>" + resources.getString(R.string.leak_canary_help_title) + "</b></font>"));
|
||||
SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) Html.fromHtml(resources.getString(R.string.leak_canary_help_detail));
|
||||
SquigglySpan.replaceUnderlineSpans(spannableStringBuilder, resources);
|
||||
textView2.setText(spannableStringBuilder);
|
||||
} else {
|
||||
boolean z2 = i == getCount() - 1;
|
||||
LeakTraceElement item = getItem(i);
|
||||
Reachability reachability = this.leakTrace.expectedReachability.get(elementIndex(i));
|
||||
if (!z2 && reachability != Reachability.UNREACHABLE && this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE) {
|
||||
z = true;
|
||||
}
|
||||
textView.setText(htmlTitle(item, z, resources));
|
||||
if (this.opened[i]) {
|
||||
textView2.setText(htmlDetails(z2, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override // android.widget.BaseAdapter, android.widget.Adapter
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public void toggleRow(int i) {
|
||||
this.opened[i] = !r0[i];
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void update(LeakTrace leakTrace, String str, String str2) {
|
||||
if (str.equals(this.referenceKey)) {
|
||||
return;
|
||||
}
|
||||
this.referenceKey = str;
|
||||
this.referenceName = str2;
|
||||
this.leakTrace = leakTrace;
|
||||
this.opened = new boolean[leakTrace.elements.size() + 2];
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public LeakTraceElement getItem(int i) {
|
||||
if (getItemViewType(i) == 0 || i == 1) {
|
||||
return null;
|
||||
}
|
||||
return this.leakTrace.elements.get(elementIndex(i));
|
||||
}
|
||||
}
|
@@ -0,0 +1,228 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class DisplayLeakConnectorView extends View {
|
||||
private Bitmap cache;
|
||||
private final float circleY;
|
||||
private final Paint classNamePaint;
|
||||
private final Paint clearPaint;
|
||||
private final Paint leakPaint;
|
||||
private final Paint referencePaint;
|
||||
private final float strokeSize;
|
||||
private Type type;
|
||||
private static final float SQRT_TWO = (float) Math.sqrt(2.0d);
|
||||
private static final PorterDuffXfermode CLEAR_XFER_MODE = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.internal.DisplayLeakConnectorView$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type = new int[Type.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_UNKNOWN.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_UNREACHABLE.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_REACHABLE.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_FIRST_UNREACHABLE.ordinal()] = 4;
|
||||
} catch (NoSuchFieldError unused4) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_LAST_REACHABLE.ordinal()] = 5;
|
||||
} catch (NoSuchFieldError unused5) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.START.ordinal()] = 6;
|
||||
} catch (NoSuchFieldError unused6) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.START_LAST_REACHABLE.ordinal()] = 7;
|
||||
} catch (NoSuchFieldError unused7) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.END.ordinal()] = 8;
|
||||
} catch (NoSuchFieldError unused8) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.END_FIRST_UNREACHABLE.ordinal()] = 9;
|
||||
} catch (NoSuchFieldError unused9) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.HELP.ordinal()] = 10;
|
||||
} catch (NoSuchFieldError unused10) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
HELP,
|
||||
START,
|
||||
START_LAST_REACHABLE,
|
||||
NODE_UNKNOWN,
|
||||
NODE_FIRST_UNREACHABLE,
|
||||
NODE_UNREACHABLE,
|
||||
NODE_REACHABLE,
|
||||
NODE_LAST_REACHABLE,
|
||||
END,
|
||||
END_FIRST_UNREACHABLE
|
||||
}
|
||||
|
||||
public DisplayLeakConnectorView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.type = Type.NODE_UNKNOWN;
|
||||
this.circleY = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_center_y);
|
||||
this.strokeSize = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_stroke_size);
|
||||
this.classNamePaint = new Paint(1);
|
||||
this.classNamePaint.setColor(resources.getColor(R.color.leak_canary_class_name));
|
||||
this.classNamePaint.setStrokeWidth(this.strokeSize);
|
||||
this.leakPaint = new Paint(1);
|
||||
this.leakPaint.setColor(resources.getColor(R.color.leak_canary_leak));
|
||||
this.leakPaint.setStyle(Paint.Style.STROKE);
|
||||
this.leakPaint.setStrokeWidth(this.strokeSize);
|
||||
this.leakPaint.setPathEffect(new DashPathEffect(new float[]{resources.getDimensionPixelSize(R.dimen.leak_canary_connector_leak_dash_line), resources.getDimensionPixelSize(R.dimen.leak_canary_connector_leak_dash_gap)}, 0.0f));
|
||||
this.clearPaint = new Paint(1);
|
||||
this.clearPaint.setColor(0);
|
||||
this.clearPaint.setXfermode(CLEAR_XFER_MODE);
|
||||
this.referencePaint = new Paint(1);
|
||||
this.referencePaint.setColor(resources.getColor(R.color.leak_canary_reference));
|
||||
this.referencePaint.setStrokeWidth(this.strokeSize);
|
||||
}
|
||||
|
||||
private void drawArrowHead(Canvas canvas, Paint paint) {
|
||||
float measuredWidth = getMeasuredWidth();
|
||||
float f = measuredWidth / 2.0f;
|
||||
float f2 = measuredWidth / 3.0f;
|
||||
float f3 = (f / 2.0f) * SQRT_TWO;
|
||||
float f4 = this.strokeSize;
|
||||
float f5 = this.circleY;
|
||||
float f6 = ((f5 - f3) - (f2 * 2.0f)) - f4;
|
||||
canvas.drawLine(f, 0.0f, f, (f5 - f2) - (f4 / 2.0f), paint);
|
||||
canvas.translate(f, f6);
|
||||
canvas.rotate(45.0f);
|
||||
canvas.drawLine(0.0f, f, f + (f4 / 2.0f), f, paint);
|
||||
canvas.drawLine(f, 0.0f, f, f, paint);
|
||||
canvas.rotate(-45.0f);
|
||||
canvas.translate(-f, -f6);
|
||||
}
|
||||
|
||||
private void drawInstanceCircle(Canvas canvas) {
|
||||
float measuredWidth = getMeasuredWidth();
|
||||
canvas.drawCircle(measuredWidth / 2.0f, this.circleY, measuredWidth / 3.0f, this.classNamePaint);
|
||||
}
|
||||
|
||||
private void drawItems(Canvas canvas, Paint paint, Paint paint2) {
|
||||
if (paint != null) {
|
||||
drawArrowHead(canvas, paint);
|
||||
}
|
||||
if (paint2 != null) {
|
||||
drawNextArrowLine(canvas, paint2);
|
||||
}
|
||||
drawInstanceCircle(canvas);
|
||||
}
|
||||
|
||||
private void drawNextArrowLine(Canvas canvas, Paint paint) {
|
||||
float measuredWidth = getMeasuredWidth() / 2.0f;
|
||||
canvas.drawLine(measuredWidth, this.circleY, measuredWidth, getMeasuredHeight(), paint);
|
||||
}
|
||||
|
||||
private void drawRoot(Canvas canvas) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
float f = measuredWidth;
|
||||
float f2 = f / 2.0f;
|
||||
float f3 = f2 - (this.strokeSize / 2.0f);
|
||||
canvas.drawRect(0.0f, 0.0f, f, f3, this.classNamePaint);
|
||||
canvas.drawCircle(0.0f, f3, f3, this.clearPaint);
|
||||
canvas.drawCircle(f, f3, f3, this.clearPaint);
|
||||
canvas.drawLine(f2, 0.0f, f2, measuredHeight, this.classNamePaint);
|
||||
}
|
||||
|
||||
private void drawStartLine(Canvas canvas) {
|
||||
float measuredWidth = getMeasuredWidth() / 2.0f;
|
||||
canvas.drawLine(measuredWidth, 0.0f, measuredWidth, this.circleY, this.classNamePaint);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
Bitmap bitmap = this.cache;
|
||||
if (bitmap != null && (bitmap.getWidth() != measuredWidth || this.cache.getHeight() != measuredHeight)) {
|
||||
this.cache.recycle();
|
||||
this.cache = null;
|
||||
}
|
||||
if (this.cache == null) {
|
||||
this.cache = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas2 = new Canvas(this.cache);
|
||||
switch (AnonymousClass1.$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[this.type.ordinal()]) {
|
||||
case 1:
|
||||
Paint paint = this.leakPaint;
|
||||
drawItems(canvas2, paint, paint);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
Paint paint2 = this.referencePaint;
|
||||
drawItems(canvas2, paint2, paint2);
|
||||
break;
|
||||
case 4:
|
||||
drawItems(canvas2, this.leakPaint, this.referencePaint);
|
||||
break;
|
||||
case 5:
|
||||
drawItems(canvas2, this.referencePaint, this.leakPaint);
|
||||
break;
|
||||
case 6:
|
||||
drawStartLine(canvas2);
|
||||
drawItems(canvas2, null, this.referencePaint);
|
||||
break;
|
||||
case 7:
|
||||
drawStartLine(canvas2);
|
||||
drawItems(canvas2, null, this.leakPaint);
|
||||
break;
|
||||
case 8:
|
||||
drawItems(canvas2, this.referencePaint, null);
|
||||
break;
|
||||
case 9:
|
||||
drawItems(canvas2, this.leakPaint, null);
|
||||
break;
|
||||
case 10:
|
||||
drawRoot(canvas2);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type " + this.type);
|
||||
}
|
||||
}
|
||||
canvas.drawBitmap(this.cache, 0.0f, 0.0f, (Paint) null);
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
if (type != this.type) {
|
||||
this.type = type;
|
||||
Bitmap bitmap = this.cache;
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
this.cache = null;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class ForegroundService extends IntentService {
|
||||
private final int notificationContentTitleResId;
|
||||
private final int notificationId;
|
||||
|
||||
public ForegroundService(String str, int i) {
|
||||
super(str);
|
||||
this.notificationContentTitleResId = i;
|
||||
this.notificationId = (int) SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
showForegroundNotification(100, 0, true, getString(R.string.leak_canary_notification_foreground_text));
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
onHandleIntentInForeground(intent);
|
||||
}
|
||||
|
||||
protected abstract void onHandleIntentInForeground(Intent intent);
|
||||
|
||||
protected void showForegroundNotification(int i, int i2, boolean z, String str) {
|
||||
startForeground(this.notificationId, LeakCanaryInternals.buildNotification(this, new Notification.Builder(this).setContentTitle(getString(this.notificationContentTitleResId)).setContentText(str).setProgress(i, i2, z)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface FragmentRefWatcher {
|
||||
|
||||
public static final class Helper {
|
||||
private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME = "com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
|
||||
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { // from class: com.squareup.leakcanary.internal.FragmentRefWatcher.Helper.1
|
||||
@Override // com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter, android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
Iterator it = Helper.this.fragmentRefWatchers.iterator();
|
||||
while (it.hasNext()) {
|
||||
((FragmentRefWatcher) it.next()).watchFragments(activity);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final List<FragmentRefWatcher> fragmentRefWatchers;
|
||||
|
||||
private Helper(List<FragmentRefWatcher> list) {
|
||||
this.fragmentRefWatchers = list;
|
||||
}
|
||||
|
||||
public static void install(Context context, RefWatcher refWatcher) {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
arrayList.add(new AndroidOFragmentRefWatcher(refWatcher));
|
||||
}
|
||||
try {
|
||||
arrayList.add((FragmentRefWatcher) Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME).getDeclaredConstructor(RefWatcher.class).newInstance(refWatcher));
|
||||
} catch (Exception unused) {
|
||||
}
|
||||
if (arrayList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(new Helper(arrayList).activityLifecycleCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
void watchFragments(Activity activity);
|
||||
}
|
31
sources/com/squareup/leakcanary/internal/FutureResult.java
Normal file
31
sources/com/squareup/leakcanary/internal/FutureResult.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class FutureResult<T> {
|
||||
private final AtomicReference<T> resultHolder = new AtomicReference<>();
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
public T get() {
|
||||
if (this.latch.getCount() <= 0) {
|
||||
return this.resultHolder.get();
|
||||
}
|
||||
throw new IllegalStateException("Call wait() and check its result");
|
||||
}
|
||||
|
||||
public void set(T t) {
|
||||
this.resultHolder.set(t);
|
||||
this.latch.countDown();
|
||||
}
|
||||
|
||||
public boolean wait(long j, TimeUnit timeUnit) {
|
||||
try {
|
||||
return this.latch.await(j, timeUnit);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Did not expect thread to be interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.squareup.leakcanary.AbstractAnalysisResultService;
|
||||
import com.squareup.leakcanary.AnalyzerProgressListener;
|
||||
import com.squareup.leakcanary.CanaryLog;
|
||||
import com.squareup.leakcanary.HeapAnalyzer;
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
|
||||
private static final String HEAPDUMP_EXTRA = "heapdump_extra";
|
||||
private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
|
||||
|
||||
public HeapAnalyzerService() {
|
||||
super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
|
||||
}
|
||||
|
||||
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> cls) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, HeapAnalyzerService.class, true);
|
||||
LeakCanaryInternals.setEnabledBlocking(context, cls, true);
|
||||
Intent intent = new Intent(context, (Class<?>) HeapAnalyzerService.class);
|
||||
intent.putExtra(LISTENER_CLASS_EXTRA, cls.getName());
|
||||
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
|
||||
ContextCompat.a(context, intent);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.ForegroundService
|
||||
protected void onHandleIntentInForeground(Intent intent) {
|
||||
if (intent == null) {
|
||||
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.", new Object[0]);
|
||||
return;
|
||||
}
|
||||
String stringExtra = intent.getStringExtra(LISTENER_CLASS_EXTRA);
|
||||
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
|
||||
AbstractAnalysisResultService.sendResultToListener(this, stringExtra, heapDump, new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses).checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize));
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.AnalyzerProgressListener
|
||||
public void onProgressUpdate(AnalyzerProgressListener.Step step) {
|
||||
int ordinal = (int) ((step.ordinal() * 100.0f) / AnalyzerProgressListener.Step.values().length);
|
||||
CanaryLog.d("Analysis in progress, working on: %s", step.name());
|
||||
String lowerCase = step.name().replace("_", " ").toLowerCase();
|
||||
showForegroundNotification(100, ordinal, false, lowerCase.substring(0, 1).toUpperCase() + lowerCase.substring(1));
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class LeakCanaryFileProvider extends FileProvider {
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import com.ijm.dataencryption.de.DataDecryptTool;
|
||||
import com.squareup.leakcanary.CanaryLog;
|
||||
import com.squareup.leakcanary.DefaultLeakDirectoryProvider;
|
||||
import com.squareup.leakcanary.LeakDirectoryProvider;
|
||||
import com.squareup.leakcanary.R;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakCanaryInternals {
|
||||
public static final String HUAWEI = "HUAWEI";
|
||||
public static final String LENOVO = "LENOVO";
|
||||
public static final String LG = "LGE";
|
||||
public static final String MEIZU = "Meizu";
|
||||
public static final String MOTOROLA = "motorola";
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "leakcanary";
|
||||
public static final String NVIDIA = "NVIDIA";
|
||||
public static final String SAMSUNG = "samsung";
|
||||
public static final String VIVO = "vivo";
|
||||
public static volatile RefWatcher installedRefWatcher;
|
||||
public static volatile Boolean isInAnalyzerProcess;
|
||||
private static volatile LeakDirectoryProvider leakDirectoryProvider;
|
||||
|
||||
private LeakCanaryInternals() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static Notification buildNotification(Context context, Notification.Builder builder) {
|
||||
builder.setSmallIcon(R.drawable.leak_canary_notification).setWhen(System.currentTimeMillis()).setOnlyAlertOnce(true);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService("notification");
|
||||
if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID, context.getString(R.string.leak_canary_notification_channel), 3));
|
||||
}
|
||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
return Build.VERSION.SDK_INT < 16 ? builder.getNotification() : builder.build();
|
||||
}
|
||||
|
||||
public static String classSimpleName(String str) {
|
||||
int lastIndexOf = str.lastIndexOf(46);
|
||||
return lastIndexOf == -1 ? str : str.substring(lastIndexOf + 1);
|
||||
}
|
||||
|
||||
public static LeakDirectoryProvider getLeakDirectoryProvider(Context context) {
|
||||
LeakDirectoryProvider leakDirectoryProvider2 = leakDirectoryProvider;
|
||||
return leakDirectoryProvider2 == null ? new DefaultLeakDirectoryProvider(context) : leakDirectoryProvider2;
|
||||
}
|
||||
|
||||
public static boolean isInServiceProcess(Context context, Class<? extends Service> cls) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
String str = packageManager.getPackageInfo(context.getPackageName(), 4).applicationInfo.processName;
|
||||
try {
|
||||
ServiceInfo serviceInfo = packageManager.getServiceInfo(new ComponentName(context, cls), DataDecryptTool.DECRYPT_DB_FILE);
|
||||
if (serviceInfo.processName.equals(str)) {
|
||||
CanaryLog.d("Did not expect service %s to run in main process %s", cls, str);
|
||||
return false;
|
||||
}
|
||||
int myPid = Process.myPid();
|
||||
ActivityManager.RunningAppProcessInfo runningAppProcessInfo = null;
|
||||
try {
|
||||
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = ((ActivityManager) context.getSystemService("activity")).getRunningAppProcesses();
|
||||
if (runningAppProcesses != null) {
|
||||
Iterator<ActivityManager.RunningAppProcessInfo> it = runningAppProcesses.iterator();
|
||||
while (true) {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
ActivityManager.RunningAppProcessInfo next = it.next();
|
||||
if (next.pid == myPid) {
|
||||
runningAppProcessInfo = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (runningAppProcessInfo != null) {
|
||||
return runningAppProcessInfo.processName.equals(serviceInfo.processName);
|
||||
}
|
||||
CanaryLog.d("Could not find running process for %d", Integer.valueOf(myPid));
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
CanaryLog.d("Could not get running app processes %d", e);
|
||||
return false;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException unused) {
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
CanaryLog.d(e2, "Could not get package info for %s", context.getPackageName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Executor newSingleThreadExecutor(String str) {
|
||||
return Executors.newSingleThreadExecutor(new LeakCanarySingleThreadFactory(str));
|
||||
}
|
||||
|
||||
public static void setEnabledAsync(Context context, final Class<?> cls, final boolean z) {
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.LeakCanaryInternals.1
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
LeakCanaryInternals.setEnabledBlocking(applicationContext, cls, z);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setEnabledBlocking(Context context, Class<?> cls, boolean z) {
|
||||
context.getPackageManager().setComponentEnabledSetting(new ComponentName(context, cls), z ? 1 : 2, 1);
|
||||
}
|
||||
|
||||
public static void setLeakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider2) {
|
||||
if (leakDirectoryProvider != null) {
|
||||
throw new IllegalStateException("Cannot set the LeakDirectoryProvider after it has already been set. Try setting it before installing the RefWatcher.");
|
||||
}
|
||||
leakDirectoryProvider = leakDirectoryProvider2;
|
||||
}
|
||||
|
||||
public static void showNotification(Context context, CharSequence charSequence, CharSequence charSequence2, PendingIntent pendingIntent, int i) {
|
||||
((NotificationManager) context.getSystemService("notification")).notify(i, buildNotification(context, new Notification.Builder(context).setContentText(charSequence2).setContentTitle(charSequence).setAutoCancel(true).setContentIntent(pendingIntent)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class LeakCanarySingleThreadFactory implements ThreadFactory {
|
||||
private final String threadName;
|
||||
|
||||
LeakCanarySingleThreadFactory(String str) {
|
||||
this.threadName = "LeakCanary-" + str;
|
||||
}
|
||||
|
||||
@Override // java.util.concurrent.ThreadFactory
|
||||
public Thread newThread(Runnable runnable) {
|
||||
return new Thread(runnable, this.threadName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class MoreDetailsView extends View {
|
||||
private final Paint iconPaint;
|
||||
private boolean opened;
|
||||
|
||||
public MoreDetailsView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.iconPaint = new Paint(1);
|
||||
this.iconPaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.leak_canary_more_stroke_width));
|
||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.leak_canary_MoreDetailsView);
|
||||
int color = obtainStyledAttributes.getColor(R.styleable.leak_canary_MoreDetailsView_leak_canary_plus_color, -16777216);
|
||||
obtainStyledAttributes.recycle();
|
||||
this.iconPaint.setColor(color);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int i = height / 2;
|
||||
int i2 = width / 2;
|
||||
if (this.opened) {
|
||||
float f = i;
|
||||
canvas.drawLine(0.0f, f, width, f, this.iconPaint);
|
||||
} else {
|
||||
float f2 = i;
|
||||
canvas.drawLine(0.0f, f2, width, f2, this.iconPaint);
|
||||
float f3 = i2;
|
||||
canvas.drawLine(f3, 0.0f, f3, height, this.iconPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOpened(boolean z) {
|
||||
if (z != this.opened) {
|
||||
this.opened = z;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
@TargetApi(23)
|
||||
/* loaded from: classes.dex */
|
||||
public class RequestStoragePermissionActivity extends Activity {
|
||||
public static PendingIntent createPendingIntent(Context context) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, RequestStoragePermissionActivity.class, true);
|
||||
Intent intent = new Intent(context, (Class<?>) RequestStoragePermissionActivity.class);
|
||||
intent.setFlags(335544320);
|
||||
return PendingIntent.getActivity(context, 1, intent, 134217728);
|
||||
}
|
||||
|
||||
private boolean hasStoragePermission() {
|
||||
return checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE") == 0;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public void finish() {
|
||||
overridePendingTransition(0, 0);
|
||||
super.finish();
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
if (bundle == null) {
|
||||
if (hasStoragePermission()) {
|
||||
finish();
|
||||
} else {
|
||||
requestPermissions(new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public void onRequestPermissionsResult(int i, String[] strArr, int[] iArr) {
|
||||
if (!hasStoragePermission()) {
|
||||
Toast.makeText(getApplication(), R.string.leak_canary_permission_not_granted, 1).show();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class RowElementLayout extends ViewGroup {
|
||||
private View connector;
|
||||
private final int connectorWidth;
|
||||
private View details;
|
||||
private final int minHeight;
|
||||
private View moreButton;
|
||||
private final int moreMarginTop;
|
||||
private final int moreSize;
|
||||
private final int rowMargins;
|
||||
private View title;
|
||||
private final int titleMarginTop;
|
||||
|
||||
public RowElementLayout(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.connectorWidth = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_width);
|
||||
this.rowMargins = resources.getDimensionPixelSize(R.dimen.leak_canary_row_margins);
|
||||
this.moreSize = resources.getDimensionPixelSize(R.dimen.leak_canary_more_size);
|
||||
this.minHeight = resources.getDimensionPixelSize(R.dimen.leak_canary_row_min);
|
||||
this.titleMarginTop = resources.getDimensionPixelSize(R.dimen.leak_canary_row_title_margin_top);
|
||||
this.moreMarginTop = resources.getDimensionPixelSize(R.dimen.leak_canary_more_margin_top);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.connector = findViewById(R.id.leak_canary_row_connector);
|
||||
this.moreButton = findViewById(R.id.leak_canary_row_more);
|
||||
this.title = findViewById(R.id.leak_canary_row_title);
|
||||
this.details = findViewById(R.id.leak_canary_row_details);
|
||||
}
|
||||
|
||||
@Override // android.view.ViewGroup, android.view.View
|
||||
protected void onLayout(boolean z, int i, int i2, int i3, int i4) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredWidth2 = this.rowMargins + this.connector.getMeasuredWidth();
|
||||
View view = this.connector;
|
||||
view.layout(this.rowMargins, 0, measuredWidth2, view.getMeasuredHeight());
|
||||
View view2 = this.moreButton;
|
||||
int i5 = this.rowMargins;
|
||||
int i6 = this.moreSize;
|
||||
int i7 = this.moreMarginTop;
|
||||
view2.layout((measuredWidth - i5) - i6, i7, measuredWidth - i5, i6 + i7);
|
||||
int i8 = measuredWidth2 + this.rowMargins;
|
||||
int measuredHeight = this.titleMarginTop + this.title.getMeasuredHeight();
|
||||
View view3 = this.title;
|
||||
view3.layout(i8, this.titleMarginTop, view3.getMeasuredWidth() + i8, measuredHeight);
|
||||
if (this.details.getVisibility() != 8) {
|
||||
View view4 = this.details;
|
||||
view4.layout(i8, measuredHeight, measuredWidth - this.rowMargins, view4.getMeasuredHeight() + measuredHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onMeasure(int i, int i2) {
|
||||
int size = View.MeasureSpec.getSize(i);
|
||||
this.title.measure(View.MeasureSpec.makeMeasureSpec(((size - this.connectorWidth) - this.moreSize) - (this.rowMargins * 4), Integer.MIN_VALUE), View.MeasureSpec.makeMeasureSpec(0, 0));
|
||||
int makeMeasureSpec = View.MeasureSpec.makeMeasureSpec(this.moreSize, 1073741824);
|
||||
this.moreButton.measure(makeMeasureSpec, makeMeasureSpec);
|
||||
int measuredHeight = this.titleMarginTop + this.title.getMeasuredHeight();
|
||||
this.details.measure(View.MeasureSpec.makeMeasureSpec((size - this.connectorWidth) - (this.rowMargins * 3), Integer.MIN_VALUE), View.MeasureSpec.makeMeasureSpec(0, 0));
|
||||
if (this.details.getVisibility() != 8) {
|
||||
measuredHeight += this.details.getMeasuredHeight();
|
||||
}
|
||||
int max = Math.max(measuredHeight, this.minHeight);
|
||||
this.connector.measure(View.MeasureSpec.makeMeasureSpec(this.connectorWidth, 1073741824), View.MeasureSpec.makeMeasureSpec(max, 1073741824));
|
||||
setMeasuredDimension(size, max);
|
||||
}
|
||||
}
|
69
sources/com/squareup/leakcanary/internal/SquigglySpan.java
Normal file
69
sources/com/squareup/leakcanary/internal/SquigglySpan.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
class SquigglySpan extends ReplacementSpan {
|
||||
private final float amplitude;
|
||||
private final float halfStrokeWidth;
|
||||
private final float halfWaveHeight;
|
||||
private final Path path;
|
||||
private final float periodDegrees;
|
||||
private final int referenceColor;
|
||||
private final Paint squigglyPaint = new Paint(1);
|
||||
private int width;
|
||||
|
||||
SquigglySpan(Resources resources) {
|
||||
this.squigglyPaint.setStyle(Paint.Style.STROKE);
|
||||
this.squigglyPaint.setColor(resources.getColor(R.color.leak_canary_leak));
|
||||
float dimensionPixelSize = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_stroke_width);
|
||||
this.squigglyPaint.setStrokeWidth(dimensionPixelSize);
|
||||
this.halfStrokeWidth = dimensionPixelSize / 2.0f;
|
||||
this.amplitude = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_amplitude);
|
||||
this.periodDegrees = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_period_degrees);
|
||||
this.path = new Path();
|
||||
this.halfWaveHeight = ((this.amplitude * 2.0f) + dimensionPixelSize) / 2.0f;
|
||||
this.referenceColor = resources.getColor(R.color.leak_canary_reference);
|
||||
}
|
||||
|
||||
public static void replaceUnderlineSpans(SpannableStringBuilder spannableStringBuilder, Resources resources) {
|
||||
for (UnderlineSpan underlineSpan : (UnderlineSpan[]) spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), UnderlineSpan.class)) {
|
||||
int spanStart = spannableStringBuilder.getSpanStart(underlineSpan);
|
||||
int spanEnd = spannableStringBuilder.getSpanEnd(underlineSpan);
|
||||
spannableStringBuilder.removeSpan(underlineSpan);
|
||||
spannableStringBuilder.setSpan(new SquigglySpan(resources), spanStart, spanEnd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void squigglyHorizontalPath(Path path, float f, float f2, float f3, float f4, float f5) {
|
||||
path.reset();
|
||||
path.moveTo(f, f3);
|
||||
float f6 = (float) (6.283185307179586d / f5);
|
||||
for (float f7 = 0.0f; f7 <= f2 - f; f7 += 1.0f) {
|
||||
path.lineTo(f + f7, (float) ((f4 * Math.sin((f6 * f7) + 40.0f)) + f3));
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.text.style.ReplacementSpan
|
||||
public void draw(Canvas canvas, CharSequence charSequence, int i, int i2, float f, int i3, int i4, int i5, Paint paint) {
|
||||
Path path = this.path;
|
||||
float f2 = this.halfStrokeWidth;
|
||||
squigglyHorizontalPath(path, f + f2, (this.width + f) - f2, i5 - this.halfWaveHeight, this.amplitude, this.periodDegrees);
|
||||
canvas.drawPath(this.path, this.squigglyPaint);
|
||||
paint.setColor(this.referenceColor);
|
||||
canvas.drawText(charSequence, i, i2, f, i4, paint);
|
||||
}
|
||||
|
||||
@Override // android.text.style.ReplacementSpan
|
||||
public int getSize(Paint paint, CharSequence charSequence, int i, int i2, Paint.FontMetricsInt fontMetricsInt) {
|
||||
this.width = (int) paint.measureText(charSequence, i, i2);
|
||||
return this.width;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user