diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
| commit | 52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98 (patch) | |
| tree | 2eb01432fa3da41550dd622d29fb672509122b51 /apps/Development/src/com/android/development | |
| parent | d4aee0c0caa00aa02d4c50ed28151591ac0456b5 (diff) | |
| download | android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.gz android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.bz2 android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.zip | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'apps/Development/src/com/android/development')
26 files changed, 3970 insertions, 0 deletions
diff --git a/apps/Development/src/com/android/development/AppHwConfigList.java b/apps/Development/src/com/android/development/AppHwConfigList.java new file mode 100755 index 000000000..52aff0dfd --- /dev/null +++ b/apps/Development/src/com/android/development/AppHwConfigList.java @@ -0,0 +1,156 @@ +/* +** +** Copyright 2006, 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.development; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class AppHwConfigList extends ListActivity { + private static final String TAG = "AppHwConfigList"; + PackageManager mPm; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mPm = getPackageManager(); + mAdapter = new AppListAdapter(this); + if (mAdapter.getCount() <= 0) { + finish(); + } else { + setListAdapter(mAdapter); + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + PackageInfo app = mAdapter.appForPosition(position); + // TODO display all preference settings + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setClass(this, AppHwPref.class); + intent.putExtra("packageName", app.packageName); + startActivity(intent); + } + + private final class AppListAdapter extends BaseAdapter { + public AppListAdapter(Context context) { + mContext = context; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + List<ApplicationInfo> appList = mPm.getInstalledApplications(0); + for (ApplicationInfo app : appList) { + PackageInfo pkgInfo = null; + try { + pkgInfo = mPm.getPackageInfo(app.packageName, 0); + } catch (NameNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if ((pkgInfo != null)) { + if(mList == null) { + mList = new ArrayList<PackageInfo>(); + } + mList.add(pkgInfo); + } + } + if (mList != null) { + Collections.sort(mList, sDisplayNameComparator); + } + } + + public PackageInfo appForPosition(int position) { + if (mList == null) { + return null; + } + return mList.get(position); + } + + public int getCount() { + return mList != null ? mList.size() : 0; + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = mInflater.inflate( + android.R.layout.simple_list_item_1, parent, false); + } else { + view = convertView; + } + bindView(view, mList.get(position)); + return view; + } + + private final void bindView(View view, PackageInfo info) { + TextView text = (TextView)view.findViewById(android.R.id.text1); + text.setText(info != null ? info.applicationInfo.loadLabel(mPm) : "(none)"); + } + + protected final Context mContext; + protected final LayoutInflater mInflater; + protected List<PackageInfo> mList; + + } + + private final Comparator sDisplayNameComparator = new Comparator() { + public final int compare(Object a, Object b) { + CharSequence sa = ((PackageInfo) a).applicationInfo.loadLabel(mPm); + CharSequence sb = ((PackageInfo) b).applicationInfo.loadLabel(mPm); + return collator.compare(sa, sb); + } + private final Collator collator = Collator.getInstance(); + }; + + private AppListAdapter mAdapter; +} + diff --git a/apps/Development/src/com/android/development/AppHwPref.java b/apps/Development/src/com/android/development/AppHwPref.java new file mode 100644 index 000000000..bf0f84fa3 --- /dev/null +++ b/apps/Development/src/com/android/development/AppHwPref.java @@ -0,0 +1,228 @@ +/* +** +** Copyright 2006, 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.development; + +import com.android.development.R; +import android.app.Activity; +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ConfigurationInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + +/* This activity displays the hardware configuration details + * of an application as defined in its manifests + */ +public class AppHwPref extends Activity { + private static final String TAG = "AppHwPref"; + PackageManager mPm; + private static final int BASE = 0; + private static final int TOUCHSCREEN = BASE + 1; + private static final int KEYBOARD_TYPE = BASE + 2; + private static final int NAVIGATION = BASE + 3; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + String pkgName = intent.getStringExtra("packageName"); + if(pkgName == null) { + handleError("Null package name", true); + return; + } + mPm = getPackageManager(); + PackageInfo pInfo; + try { + pInfo = mPm.getPackageInfo(pkgName, PackageManager.GET_CONFIGURATIONS); + } catch (NameNotFoundException e) { + pInfo = null; + } + if(pInfo == null) { + handleError("Failed retrieving packageInfo for pkg:"+pkgName, true); + return; + } + ConfigurationInfo appHwPref[] = pInfo.configPreferences; + + setContentView(R.layout.application_hw_pref); + if(appHwPref != null) { + displayTextView(R.id.attr_package, pInfo.applicationInfo.loadLabel(mPm)); + displayTextView(R.id.attr_touchscreen, appHwPref, TOUCHSCREEN); + displayTextView(R.id.attr_input_method, appHwPref, KEYBOARD_TYPE); + displayTextView(R.id.attr_navigation, appHwPref, NAVIGATION); + displayFlag(R.id.attr_hard_keyboard, ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD, appHwPref); + displayFlag(R.id.attr_five_way_nav, ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV, appHwPref); + } + } + + void displayFlag(int viewId, int flagMask, ConfigurationInfo[] appHwPref) { + if(appHwPref == null) { + return; + } + boolean flag = false; + for (int i = 0; i < appHwPref.length; i++) { + ConfigurationInfo pref = appHwPref[i]; + if((pref.reqInputFeatures & flagMask) != 0) { + flag = true; + break; + } + } + if(flag) { + displayTextView(viewId, "true"); + } else { + displayTextView(viewId, "false"); + } + } + + void handleError(String errMsg, boolean finish) { + // TODO display dialog + Log.i(TAG, errMsg); + if(finish) { + finish(); + } + } + + void displayTextView(int textViewId, CharSequence displayStr) { + TextView tView = (TextView) findViewById(textViewId); + if(displayStr != null) { + tView.setText(displayStr); + } + } + + void displayTextView(int viewId, ConfigurationInfo[] config, int type) { + if((config == null) || (config.length < 1)) { + return; + } + + HashSet<String> list = new HashSet<String>(); + for(int i = 0; i < config.length; i++) { + String str = null; + switch(type) { + case TOUCHSCREEN: + str = getTouchScreenStr(config[i]); + break; + case KEYBOARD_TYPE: + str = getKeyboardTypeStr(config[i]); + break; + case NAVIGATION: + str = getNavigationStr(config[i]); + break; + } + if(str != null) { + list.add(str); + } + } + String listStr = ""; + boolean set = false; + for(String str : list) { + set = true; + listStr += str+","; + } + if(set) { + TextView tView = (TextView)findViewById(viewId); + CharSequence txt = listStr.subSequence(0, listStr.length()-1); + tView.setText(txt); + } + } + + String getTouchScreenStr(ConfigurationInfo appHwPref) { + if(appHwPref == null) { + handleError("Invalid HardwareConfigurationObject", true); + return null; + } + switch(appHwPref.reqTouchScreen) { + case Configuration.TOUCHSCREEN_FINGER: + return "finger"; + case Configuration.TOUCHSCREEN_NOTOUCH: + return "notouch"; + case Configuration.TOUCHSCREEN_STYLUS: + return "stylus"; + case Configuration.TOUCHSCREEN_UNDEFINED: + return null; + default: + return null; + } + } + + String getKeyboardTypeStr(ConfigurationInfo appHwPref) { + if(appHwPref == null) { + handleError("Invalid HardwareConfigurationObject", true); + return null; + } + switch(appHwPref.reqKeyboardType) { + case Configuration.KEYBOARD_12KEY: + return "12key"; + case Configuration.KEYBOARD_NOKEYS: + return "nokeys"; + case Configuration.KEYBOARD_QWERTY: + return "querty"; + case Configuration.KEYBOARD_UNDEFINED: + return null; + default: + return null; + } + } + + String getNavigationStr(ConfigurationInfo appHwPref) { + if(appHwPref == null) { + handleError("Invalid HardwareConfigurationObject", true); + return null; + } + switch(appHwPref.reqNavigation) { + case Configuration.NAVIGATION_DPAD: + return "dpad"; + case Configuration.NAVIGATION_TRACKBALL: + return "trackball"; + case Configuration.NAVIGATION_WHEEL: + return "wheel"; + case Configuration.NAVIGATION_UNDEFINED: + return null; + default: + return null; + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onStop() { + super.onStop(); + } +} + diff --git a/apps/Development/src/com/android/development/AppPicker.java b/apps/Development/src/com/android/development/AppPicker.java new file mode 100644 index 000000000..693defbf9 --- /dev/null +++ b/apps/Development/src/com/android/development/AppPicker.java @@ -0,0 +1,167 @@ +/* //device/java/android/android/app/ResolveListActivity.java +** +** Copyright 2006, 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.development; + +import android.app.ActivityManagerNative; +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.os.RemoteException; +import android.provider.Settings; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.text.Collator; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class AppPicker extends ListActivity +{ + @Override + protected void onCreate(Bundle icicle) + { + super.onCreate(icicle); + + mAdapter = new AppListAdapter(this); + if (mAdapter.getCount() <= 0) { + finish(); + } else { + setListAdapter(mAdapter); + } + } + + @Override + protected void onResume() + { + super.onResume(); + } + + @Override + protected void onStop() + { + super.onStop(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) + { + ApplicationInfo app = mAdapter.appForPosition(position); + Intent intent = new Intent(); + if (app != null) intent.setAction(app.packageName); + setResult(RESULT_OK, intent); + + /* This is a temporary fix for 824637 while it is blocked by 805226. When 805226 is resolved, please remove this. */ + try { + boolean waitForDebugger = Settings.System.getInt( + getContentResolver(), Settings.System.WAIT_FOR_DEBUGGER, 0) != 0; + ActivityManagerNative.getDefault().setDebugApp( + app != null ? app.packageName : null, waitForDebugger, true); + } catch (RemoteException ex) { + } + + finish(); + } + + private final class AppListAdapter extends BaseAdapter + { + public AppListAdapter(Context context) + { + mContext = context; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + mList = context.getPackageManager().getInstalledApplications(0); + if (mList != null) { + Collections.sort(mList, sDisplayNameComparator); + mList.add(0, null); + } + } + + public ApplicationInfo appForPosition(int position) + { + if (mList == null) { + return null; + } + + return mList.get(position); + } + + public int getCount() + { + return mList != null ? mList.size() : 0; + } + + public Object getItem(int position) + { + return position; + } + + public long getItemId(int position) + { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) + { + View view; + if (convertView == null) { + view = mInflater.inflate( + android.R.layout.simple_list_item_1, parent, false); + } else { + view = convertView; + } + bindView(view, mList.get(position)); + return view; + } + + private final void bindView(View view, ApplicationInfo info) + { + TextView text = (TextView)view.findViewById(android.R.id.text1); + + text.setText(info != null ? info.packageName : "(none)"); + } + + protected final Context mContext; + protected final LayoutInflater mInflater; + + protected List<ApplicationInfo> mList; + + } + + private final static Comparator sDisplayNameComparator = new Comparator() { + public final int + compare(Object a, Object b) + { + CharSequence sa = ((ApplicationInfo) a).packageName; + CharSequence sb = ((ApplicationInfo) b).packageName; + + return collator.compare(sa, sb); + } + + private final Collator collator = Collator.getInstance(); + }; + + private AppListAdapter mAdapter; +} + diff --git a/apps/Development/src/com/android/development/ArrayAdapter.java b/apps/Development/src/com/android/development/ArrayAdapter.java new file mode 100644 index 000000000..378e98f24 --- /dev/null +++ b/apps/Development/src/com/android/development/ArrayAdapter.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; + +import java.util.List; + +public abstract class ArrayAdapter<E> extends BaseAdapter +{ + public ArrayAdapter(Context context, int layoutRes) { + mContext = context; + mInflater = (LayoutInflater)context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mLayoutRes = layoutRes; + } + + public void setSource(List<E> list) { + mList = list; + } + + public abstract void bindView(View view, E item); + + public E itemForPosition(int position) { + if (mList == null) { + return null; + } + + return mList.get(position); + } + + public int getCount() { + return mList != null ? mList.size() : 0; + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = mInflater.inflate(mLayoutRes, parent, false); + } else { + view = convertView; + } + bindView(view, mList.get(position)); + return view; + } + + private final Context mContext; + private final LayoutInflater mInflater; + private final int mLayoutRes; + private List<E> mList; +} + diff --git a/apps/Development/src/com/android/development/ColumnData.java b/apps/Development/src/com/android/development/ColumnData.java new file mode 100644 index 000000000..28781c2d7 --- /dev/null +++ b/apps/Development/src/com/android/development/ColumnData.java @@ -0,0 +1,31 @@ +/* //device/apps/Notes/NotesList.java +** +** Copyright 2006, 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.development; + +import java.util.ArrayList; + +class ColumnData +{ + ColumnData(String k, String v) + { + key = k; + value = v; + } + public String key; + public String value; +} + diff --git a/apps/Development/src/com/android/development/DataList.java b/apps/Development/src/com/android/development/DataList.java new file mode 100644 index 000000000..5dee2fe9c --- /dev/null +++ b/apps/Development/src/com/android/development/DataList.java @@ -0,0 +1,134 @@ +/* //device/apps/Notes/NotesList.java +** +** Copyright 2006, 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.development; + +import android.app.ListActivity; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; + +import java.util.ArrayList; + +public class DataList extends ListActivity +{ + public void onCreate(Bundle icicle) + { + super.onCreate(icicle); + + Intent intent = getIntent(); + + mCursor = getContentResolver().query(intent.getData(), null, null, null, null); + mDisplay = intent.getStringExtra("display"); + if (mDisplay == null) { + mDisplay = "_id"; + } + + if (mCursor != null) { + setListAdapter(new SimpleCursorAdapter( + this, + R.layout.url_list, + mCursor, + new String[] {mDisplay}, + new int[] {android.R.id.text1})); + } + } + + public void onStop() + { + super.onStop(); + + if (mCursor != null) { + mCursor.deactivate(); + } + } + + public void onResume() + { + super.onResume(); + + if (mCursor != null) { + mCursor.requery(); + } + + setTitle("Showing " + mDisplay); + } + + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + menu.add(0, 0, 0, "Requery").setOnMenuItemClickListener(mRequery); + return true; + } + + protected void onListItemClick(ListView l, View v, int position, long id) + { + mCursor.moveToPosition(position); + + ArrayList<ColumnData> data = new ArrayList<ColumnData>(); + + String[] columnNames = mCursor.getColumnNames(); + for (int i=0; i<columnNames.length; i++) { + String str = mCursor.getString(i); + ColumnData cd = new ColumnData(columnNames[i], str); + data.add(cd); + } + + + Uri uri = null; + int idCol = mCursor.getColumnIndex("_id"); + if (idCol >= 0) { + uri = Uri.withAppendedPath(getIntent().getData(), mCursor.getString(idCol)); + } + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.setClass(this, Details.class); + + intent.putExtra("data", data); + int displayColumn = mCursor.getColumnIndex(mDisplay); + if (displayColumn >= 0) { + intent.putExtra("title", + ((ColumnData)data.get(displayColumn)).value); + } + + startActivity(intent); + } + + MenuItem.OnMenuItemClickListener mRequery = new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // Should just do requery on cursor, but + // doesn't work right now. So do this instead. + mCursor.requery(); + if (mCursor != null) { + setListAdapter(new SimpleCursorAdapter( + DataList.this, + R.layout.url_list, + mCursor, + new String[] {mDisplay}, + new int[] {android.R.id.text1})); + } + return true; + } + }; + + private String mDisplay; + private Cursor mCursor; +} diff --git a/apps/Development/src/com/android/development/Details.java b/apps/Development/src/com/android/development/Details.java new file mode 100644 index 000000000..16722a3ab --- /dev/null +++ b/apps/Development/src/com/android/development/Details.java @@ -0,0 +1,157 @@ +/* //device/apps/Notes/NotesList.java +** +** Copyright 2006, 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.development; + +import java.util.ArrayList; + +import android.content.Intent; +import android.app.Activity; +import android.database.Cursor; +import android.graphics.Typeface; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class Details extends Activity +{ + public void onCreate(Bundle icicle) + { + super.onCreate(icicle); + + Intent intent = getIntent(); + + String title = intent.getStringExtra("title"); + if (title == null) { + title = "Details"; + } + setTitle(title); + + mScrollView = new ScrollView(this); + setContentView(mScrollView); + mScrollView.setFocusable(true); + + mData = (ArrayList<ColumnData>)getIntent().getExtra("data"); + addDataViews(); + } + + public void onResume() + { + super.onResume(); + } + + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + menu.add(0, 0, 0, "Requery").setOnMenuItemClickListener(mRequery); + menu.add(0, 0, 0, "Print to stdout").setOnMenuItemClickListener(mPrintToStdout); + return true; + } + + void addDataViews() + { + int oldScroll = 0; + + if (mLinearLayout != null) { + mScrollView.removeView(mLinearLayout); + } + mLinearLayout = new LinearLayout(this); + mScrollView.addView(mLinearLayout, new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.FILL_PARENT)); + mLinearLayout.setOrientation(LinearLayout.VERTICAL); + + // Here in onStart, we're given data. We use that because some + // data that we show is transient and can't be retrieved from a url. + // We'll try to use that in requery + int count = mData.size(); + for (int i=0; i<count; i++) { + ColumnData cd = mData.get(i); + TextView label = makeView(cd.key, true, 12); + TextView contents = makeView(cd.value, false, 12); + contents.setPadding(3, 0, 0, i==count-1?0:3); + mLinearLayout.addView(label, lazy()); + mLinearLayout.addView(contents, lazy()); + } + } + + TextView makeView(String str, boolean bold, int fontSize) + { + if (str == null) { + str = "(null)"; + } + TextView v = new TextView(this); + v.setText(str); + v.setTextSize(fontSize); + if (bold) { + v.setTypeface(Typeface.DEFAULT_BOLD); + } + return v; + } + + LinearLayout.LayoutParams lazy() + { + return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, 0); + } + + MenuItem.OnMenuItemClickListener mRequery = new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + Intent intent = getIntent(); + Cursor c = getContentResolver().query(intent.getData(), null, null, null, null); + if (c != null && c.moveToNext()) { + mData.clear(); + String[] columnNames = c.getColumnNames(); + for (int i=0; i<columnNames.length; i++) { + String str = c.getString(i); + ColumnData cd = new ColumnData(columnNames[i], str); + mData.add(cd); + } + addDataViews(); + } else { + TextView error = new TextView(Details.this); + error.setText("Showing old data.\nURL couldn't be requeried:\n" + + intent.getData()); + error.setTextColor(0xffff0000); + error.setTextSize(11); + mLinearLayout.addView(error, 0, lazy()); + } + return true; + } + }; + + MenuItem.OnMenuItemClickListener mPrintToStdout = new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + System.out.println("=== begin data ==="); + int count = mData.size(); + for (int i=0; i<count; i++) { + ColumnData cd = mData.get(i); + System.out.println(" " + cd.key + ": " + cd.value); + } + System.out.println("=== end data ==="); + return true; + } + }; + + LinearLayout mLinearLayout; + ScrollView mScrollView; + ArrayList<ColumnData> mData; +} diff --git a/apps/Development/src/com/android/development/Development.java b/apps/Development/src/com/android/development/Development.java new file mode 100644 index 000000000..a3979f219 --- /dev/null +++ b/apps/Development/src/com/android/development/Development.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.LauncherActivity; +import android.content.Intent; + +public class Development extends LauncherActivity +{ + @Override + protected Intent getTargetIntent() { + Intent targetIntent = new Intent(Intent.ACTION_MAIN, null); + targetIntent.addCategory(Intent.CATEGORY_TEST); + targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return targetIntent; + } +} diff --git a/apps/Development/src/com/android/development/DevelopmentSettings.java b/apps/Development/src/com/android/development/DevelopmentSettings.java new file mode 100644 index 000000000..cdc1e0a27 --- /dev/null +++ b/apps/Development/src/com/android/development/DevelopmentSettings.java @@ -0,0 +1,510 @@ +/* //device/apps/Settings/src/com/android/settings/Keyguard.java +** +** Copyright 2006, 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.development; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.RemoteException; +import android.os.IBinder; +import android.os.Parcel; +import android.os.ServiceManager; +import android.os.ServiceManagerNative; +import android.provider.Settings; +import android.os.Bundle; +import android.util.Log; +import android.view.IWindowManager; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.Spinner; +import android.widget.AdapterView.OnItemSelectedListener; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Map; + +public class DevelopmentSettings extends Activity { + private static final String TAG = "DevelopmentSettings"; + private static final int DEBUG_APP_REQUEST = 1; + + private Button mDebugAppButton; + private CheckBox mWaitForDebuggerCB; + private CheckBox mAlwaysFinishCB; + private CheckBox mShowLoadCB; + private CheckBox mShowCpuCB; + private CheckBox mEnableGLCB; + private CheckBox mShowUpdatesCB; + private CheckBox mShowBackgroundCB; + private CheckBox mShowSleepCB; + private CheckBox mShowMapsCompassCB; + private CheckBox mShowXmppCB; + private Spinner mMaxProcsSpinner; + private Spinner mWindowAnimationScaleSpinner; + private Spinner mTransitionAnimationScaleSpinner; + private Spinner mFontHintingSpinner; + + private String mDebugApp; + private boolean mWaitForDebugger; + private boolean mAlwaysFinish; + private int mProcessLimit; + private boolean mShowSleep; + private boolean mShowMapsCompass; + private boolean mShowXmpp; + private AnimationScaleSelectedListener mWindowAnimationScale + = new AnimationScaleSelectedListener(0); + private AnimationScaleSelectedListener mTransitionAnimationScale + = new AnimationScaleSelectedListener(1); + private SharedPreferences mSharedPrefs; + private IWindowManager mWindowManager; + + private static final boolean FONT_HINTING_ENABLED = true; + private static final String FONT_HINTING_FILE = "/data/misc/font-hack"; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.development_settings); + + mDebugAppButton = (Button)findViewById(R.id.debug_app); + mDebugAppButton.setOnClickListener(mDebugAppClicked); + mWaitForDebuggerCB = (CheckBox)findViewById(R.id.wait_for_debugger); + mWaitForDebuggerCB.setOnClickListener(mWaitForDebuggerClicked); + mAlwaysFinishCB = (CheckBox)findViewById(R.id.always_finish); + mAlwaysFinishCB.setOnClickListener(mAlwaysFinishClicked); + mShowLoadCB = (CheckBox)findViewById(R.id.show_load); + mShowLoadCB.setOnClickListener(mShowLoadClicked); + mShowCpuCB = (CheckBox)findViewById(R.id.show_cpu); + mShowCpuCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1000)); + mEnableGLCB = (CheckBox)findViewById(R.id.enable_gl); + mEnableGLCB.getLayoutParams().height = 0; // doesn't do anything + mEnableGLCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1004)); + mShowUpdatesCB = (CheckBox)findViewById(R.id.show_updates); + mShowUpdatesCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1002)); + mShowBackgroundCB = (CheckBox)findViewById(R.id.show_background); + mShowBackgroundCB.setOnCheckedChangeListener(new SurfaceFlingerClicker(1003)); + mShowSleepCB = (CheckBox)findViewById(R.id.show_sleep); + mShowSleepCB.setOnClickListener(mShowSleepClicked); + mShowMapsCompassCB = (CheckBox)findViewById(R.id.show_maps_compass); + mShowMapsCompassCB.setOnClickListener(mShowMapsCompassClicked); + mShowXmppCB = (CheckBox)findViewById(R.id.show_xmpp); + mShowXmppCB.setOnClickListener(mShowXmppClicked); + mMaxProcsSpinner = (Spinner)findViewById(R.id.max_procs); + mMaxProcsSpinner.setOnItemSelectedListener(mMaxProcsChanged); + ArrayAdapter<String> adapter = new ArrayAdapter<String>( + this, + android.R.layout.simple_spinner_item, + new String[] { + "No App Process Limit", + "Max 1 App Process", + "Max 2 App Processes", + "Max 3 App Processes", + "Max 4 App Processes" }); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mMaxProcsSpinner.setAdapter(adapter); + mWindowAnimationScaleSpinner = setupAnimationSpinner( + R.id.window_animation_scale, mWindowAnimationScale, "Window"); + mTransitionAnimationScaleSpinner = setupAnimationSpinner( + R.id.transition_animation_scale, mTransitionAnimationScale, "Transition"); + + if (FONT_HINTING_ENABLED) { + mFontHintingSpinner = (Spinner)findViewById(R.id.font_hinting); + mFontHintingSpinner.setOnItemSelectedListener(mFontHintingChanged); + adapter = new ArrayAdapter<String>( + this, + android.R.layout.simple_spinner_item, + new String[] { + "Light Hinting", + "Medium Hinting" }); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mFontHintingSpinner.setAdapter(adapter); + } + mSharedPrefs = getSharedPreferences("global", 0); + mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + } + + Spinner setupAnimationSpinner(int resid, + AnimationScaleSelectedListener listener, String name) { + Spinner spinner = (Spinner)findViewById(resid); + spinner.setOnItemSelectedListener(listener); + ArrayAdapter adapter = new ArrayAdapter<String>( + this, + android.R.layout.simple_spinner_item, + new String[] { + name + " Animation Scale 1x", + name + " Animation Scale 2x", + name + " Animation Scale 5x", + name + " Animation Scale 10x", + name + " Animation Off" }); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + listener.spinner = spinner; + return spinner; + } + + @Override + public void onResume() { + super.onResume(); + updateDebugOptions(); + updateFinishOptions(); + updateProcessLimitOptions(); + updateSharedOptions(); + updateFlingerOptions(); + updateSleepOptions(); + updateMapsCompassOptions(); + updateXmppOptions(); + + try { + FileInputStream in = new FileInputStream( FONT_HINTING_FILE ); + int mode = in.read() - 48; + if (mode >= 0 && mode < 3) + mFontHintingSpinner.setSelection(mode); + in.close(); + } catch (Exception e) { + } + + mWindowAnimationScale.load(); + mTransitionAnimationScale.load(); + } + + private void writeDebugOptions() { + try { + ActivityManagerNative.getDefault().setDebugApp( + mDebugApp, mWaitForDebugger, true); + } catch (RemoteException ex) { + } + } + + private void updateDebugOptions() { + mDebugApp = Settings.System.getString( + getContentResolver(), Settings.System.DEBUG_APP); + mWaitForDebugger = Settings.System.getInt( + getContentResolver(), Settings.System.WAIT_FOR_DEBUGGER, 0) != 0; + + mDebugAppButton.setText( + mDebugApp == null || mDebugApp.length() == 0 ? "(none)" : mDebugApp); + mWaitForDebuggerCB.setChecked(mWaitForDebugger); + } + + private void writeFinishOptions() { + try { + ActivityManagerNative.getDefault().setAlwaysFinish(mAlwaysFinish); + } catch (RemoteException ex) { + } + } + + private void updateFinishOptions() { + mAlwaysFinish = Settings.System.getInt( + getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) != 0; + mAlwaysFinishCB.setChecked(mAlwaysFinish); + } + + private void writeProcessLimitOptions() { + try { + ActivityManagerNative.getDefault().setProcessLimit(mProcessLimit); + } catch (RemoteException ex) { + } + } + + private void updateProcessLimitOptions() { + try { + mProcessLimit = ActivityManagerNative.getDefault().getProcessLimit(); + mMaxProcsSpinner.setSelection(mProcessLimit); + } catch (RemoteException ex) { + } + } + + private void updateSharedOptions() { + mShowLoadCB.setChecked(Settings.System.getInt(getContentResolver(), + Settings.System.SHOW_PROCESSES, 0) != 0); + } + + private void updateFlingerOptions() { + // magic communication with surface flinger. + try { + IBinder flinger = ServiceManager.getService("SurfaceFlinger"); + if (flinger != null) { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + flinger.transact(1010, data, reply, 0); + int v; + v = reply.readInt(); + mShowCpuCB.setChecked(v != 0); + v = reply.readInt(); + mEnableGLCB.setChecked(v != 0); + v = reply.readInt(); + mShowUpdatesCB.setChecked(v != 0); + v = reply.readInt(); + mShowBackgroundCB.setChecked(v != 0); + reply.recycle(); + data.recycle(); + } + } catch (RemoteException ex) { + } + } + + private void writeSleepOptions() { + try { + FileOutputStream os = new FileOutputStream( + "/sys/devices/platform/gpio_sleep_debug/enable", true); + if(mShowSleep) + os.write(new byte[] { (byte)'1' }); + else + os.write(new byte[] { (byte)'0' }); + os.close(); + } catch (Exception e) { + Log.w(TAG, "Failed setting gpio_sleep_debug"); + } + } + + private void updateSleepOptions() { + try { + FileInputStream is = new FileInputStream( + "/sys/devices/platform/gpio_sleep_debug/enable"); + int character = is.read(); + mShowSleep = character == '1'; + is.close(); + } catch (Exception e) { + Log.w(TAG, "Failed reading gpio_sleep_debug"); + mShowSleep = false; + } + mShowSleepCB.setChecked(mShowSleep); + } + + private void writeMapsCompassOptions() { + try { + Context c = createPackageContext("com.google.android.apps.maps", 0); + c.getSharedPreferences("extra-features", MODE_WORLD_WRITEABLE) + .edit() + .putBoolean("compass", mShowMapsCompass) + .commit(); + } catch (NameNotFoundException e) { + Log.w(TAG, "Failed setting maps compass"); + e.printStackTrace(); + } + } + + private void updateMapsCompassOptions() { + try { + Context c = createPackageContext("com.google.android.apps.maps", 0); + mShowMapsCompass = c.getSharedPreferences("extra-features", MODE_WORLD_READABLE) + .getBoolean("compass", false); + } catch (NameNotFoundException e) { + Log.w(TAG, "Failed reading maps compass"); + e.printStackTrace(); + } + mShowMapsCompassCB.setChecked(mShowMapsCompass); + } + + private void writeXmppOptions() { + Settings.System.setShowGTalkServiceStatus(getContentResolver(), mShowXmpp); + } + + private void updateXmppOptions() { + mShowXmpp = Settings.System.getShowGTalkServiceStatus(getContentResolver()); + mShowXmppCB.setChecked(mShowXmpp); + } + + private View.OnClickListener mDebugAppClicked = new View.OnClickListener() { + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(DevelopmentSettings.this, AppPicker.class); + startActivityForResult(intent, DEBUG_APP_REQUEST); + } + }; + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + if (requestCode == DEBUG_APP_REQUEST && resultCode == RESULT_OK) { + mDebugApp = intent.getAction(); + writeDebugOptions(); + updateDebugOptions(); + } + } + + private View.OnClickListener mWaitForDebuggerClicked = + new View.OnClickListener() { + public void onClick(View v) { + mWaitForDebugger = ((CheckBox)v).isChecked(); + writeDebugOptions(); + updateDebugOptions(); + } + }; + + private View.OnClickListener mAlwaysFinishClicked = + new View.OnClickListener() { + public void onClick(View v) { + mAlwaysFinish = ((CheckBox)v).isChecked(); + writeFinishOptions(); + updateFinishOptions(); + } + }; + + private View.OnClickListener mShowLoadClicked = new View.OnClickListener() { + public void onClick(View v) { + boolean value = ((CheckBox)v).isChecked(); + Settings.System.putInt(getContentResolver(), + Settings.System.SHOW_PROCESSES, value ? 1 : 0); + Intent service = (new Intent()) + .setClassName("android", "com.android.server.LoadAverageService"); + if (value) { + startService(service); + } else { + stopService(service); + } + } + }; + + private class SurfaceFlingerClicker implements CheckBox.OnCheckedChangeListener { + SurfaceFlingerClicker(int code) { + mCode = code; + } + + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + try { + IBinder flinger = ServiceManager.getService("SurfaceFlinger"); + if (flinger != null) { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(isChecked ? 1 : 0); + flinger.transact(mCode, data, null, 0); + data.recycle(); + + updateFlingerOptions(); + } + } catch (RemoteException ex) { + } + } + + final int mCode; + } + + private View.OnClickListener mShowSleepClicked = + new View.OnClickListener() { + public void onClick(View v) { + mShowSleep = ((CheckBox)v).isChecked(); + writeSleepOptions(); + updateSleepOptions(); + } + }; + + private View.OnClickListener mShowMapsCompassClicked = + new View.OnClickListener() { + public void onClick(View v) { + mShowMapsCompass = ((CheckBox)v).isChecked(); + writeMapsCompassOptions(); + updateMapsCompassOptions(); + } + }; + + + private View.OnClickListener mShowXmppClicked = new View.OnClickListener() { + public void onClick(View v) { + mShowXmpp = ((CheckBox)v).isChecked(); + // can streamline these calls, but keeping consistent with the + // other development settings code. + writeXmppOptions(); + updateXmppOptions(); + } + }; + + private Spinner.OnItemSelectedListener mMaxProcsChanged + = new Spinner.OnItemSelectedListener() { + public void onItemSelected(android.widget.AdapterView av, View v, + int position, long id) { + mProcessLimit = position; + writeProcessLimitOptions(); + } + + public void onNothingSelected(android.widget.AdapterView av) { + } + }; + + private Spinner.OnItemSelectedListener mFontHintingChanged + = new Spinner.OnItemSelectedListener() { + public void onItemSelected(android.widget.AdapterView av, View v, + int position, long id) { + try { + FileOutputStream out = new FileOutputStream( FONT_HINTING_FILE ); + out.write(position+48); + out.close(); + } catch (Exception e) { + Log.w(TAG, "Failed to write font hinting settings to /data/misc/font-hack"); + } + } + + public void onNothingSelected(android.widget.AdapterView av) { + } + }; + + class AnimationScaleSelectedListener implements OnItemSelectedListener { + final int which; + float scale; + Spinner spinner; + + AnimationScaleSelectedListener(int _which) { + which = _which; + } + + void load() { + try { + scale = mWindowManager.getAnimationScale(which); + + if (scale > 0.1f && scale < 2.0f) { + spinner.setSelection(0); + } else if (scale >= 2.0f && scale < 3.0f) { + spinner.setSelection(1); + } else if (scale >= 4.9f && scale < 6.0f) { + spinner.setSelection(2); + } else if (scale >= 9.9f && scale < 11.0f) { + spinner.setSelection(3); + } else { + spinner.setSelection(4); + } + } catch (RemoteException e) { + } + } + + public void onItemSelected(android.widget.AdapterView av, View v, + int position, long id) { + switch (position) { + case 0: scale = 1.0f; break; + case 1: scale = 2.0f; break; + case 2: scale = 5.0f; break; + case 3: scale = 10.0f; break; + case 4: scale = 0.0f; break; + default: break; + } + + try { + mWindowManager.setAnimationScale(which, scale); + } catch (RemoteException e) { + } + } + + public void onNothingSelected(android.widget.AdapterView av) { + } + } +} diff --git a/apps/Development/src/com/android/development/EnterURL.java b/apps/Development/src/com/android/development/EnterURL.java new file mode 100644 index 000000000..76b8a34c5 --- /dev/null +++ b/apps/Development/src/com/android/development/EnterURL.java @@ -0,0 +1,310 @@ +/* //device/apps/Notes/NotesList.java +** +** Copyright 2006, 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.development; + +import android.app.ListActivity; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.os.Bundle; +import android.text.Editable; +import android.text.Selection; +import android.text.format.DateUtils; +import android.util.AttributeSet; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.graphics.Rect; + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.GregorianCalendar; +import java.util.HashMap; + +public class EnterURL extends ListActivity +{ + private static final int DATABASE_VERSION = 1; + public static class UrlEditText extends EditText + { + public UrlEditText(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) + { + super.onFocusChanged(focused, direction, previouslyFocusedRect); + if (focused) { + Editable text = getText(); + String str = text.toString(); + int highlightStart = 0; + if (str.startsWith("content://")) { + highlightStart = "content://".length(); + } + Selection.setSelection(text, highlightStart, text.length()); + } + } + } + + public static class DisplayEditText extends EditText + { + public DisplayEditText(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) + { + super.onFocusChanged(focused, direction, previouslyFocusedRect); + if (focused) { + Editable text = getText(); + Selection.setSelection(text, 0, text.length()); + } + } + } + + @Override + public View onCreateView(String name, Context context, AttributeSet attrs) + { + if (name.equals("com.android.development.UrlEditText")) { + return new UrlEditText(this, attrs); + } + if (name.equals("com.android.development.DisplayEditText")) { + return new DisplayEditText(this, attrs); + } + return null; + } + + View.OnClickListener mViewItemAction = new View.OnClickListener () { + public void onClick(View v) + { + String url = mUrlField.getText().toString(); + String display = mDisplayField.getText().toString(); + viewItem(url, display); + } + }; + + public void onCreate(Bundle icicle) + { + super.onCreate(icicle); + setContentView(R.layout.enter_url); + + // display + mDisplayField = (DisplayEditText)findViewById(R.id.display_edit_text); + mDisplayField.setOnClickListener(mViewItemAction); + // url + mUrlField = (UrlEditText)findViewById(R.id.url_edit_text); + mUrlField.setOnClickListener(mViewItemAction); + } + + public void onStop() + { + super.onStop(); + + if (mCursor != null) { + mCursor.deactivate(); + } + } + + public void onResume() + { + super.onResume(); + + // show the history + loadPrefs(); + fillListView(); + if (mHistory.size() > 0) { + ListView lv = this.getListView(); + lv.setSelection(0); + lv.requestFocus(); + } + } + + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + menu.add(0, 0, 0, "Clear Bookmarks").setOnMenuItemClickListener(mClearBookmarks); + return true; + } + + protected void onListItemClick(ListView l, View v, int position, long id) + { + HistoryEntry he = mHistory.get(position); + viewItem(he.url, he.display); + } + + private final void viewItem(String url, String display) + { + // -------------- save this in the history ---------------- + // look in the history + int count = mHistory.size(); + int i; + for (i=0; i<count; i++) { + HistoryEntry he = mHistory.get(i); + if (he.url.equals(url) && he.display.equals(display)) { + he.updateAccessTime(); + mHistory.remove(i); + mHistory.add(0, he); + break; + } + } + if (i >= count) { + // didn't find it, add it first + HistoryEntry he = new HistoryEntry(); + he.url = url; + he.display = display; + he.updateAccessTime(); + mHistory.add(0, he); + } + + savePrefs(); + + // -------------- view it --------------------------------- + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setClass(this, DataList.class); + intent.putExtra("display", display); + startActivity(intent); + } + + MenuItem.OnMenuItemClickListener mClearBookmarks = new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + mHistory.clear(); + savePrefs(); + fillListView(); + return true; + } + }; + + private void fillListView() + { + loadPrefs(); + ArrayList<HashMap<String, String>> d = new ArrayList<HashMap<String, String>>(); + int count = mHistory.size(); + for (int i=0; i<count; i++) { + HashMap<String, String> m = new HashMap<String, String>(); + HistoryEntry he = mHistory.get(i); + m.put("title", he.url + " (" + he.display + ")"); + d.add(m); + } + setListAdapter(new SimpleAdapter(this, d, R.layout.url_list, + new String[] {"title"}, + new int[] {android.R.id.text1})); + } + + SQLiteDatabase openDB() + { + SQLiteDatabase db = null; + db = openOrCreateDatabase("inspector.db", 0, null); + int version = db.getVersion(); + if (version != DATABASE_VERSION) { + db.execSQL("CREATE TABLE History (" + + " url TEXT," + + " display TEXT," + + " lastAccessTime TEXT" + + ");"); + db.execSQL("CREATE TABLE FieldState (" + + " url TEXT," + + " display TEXT" + + ");"); + db.setVersion(DATABASE_VERSION); + } + return db; + } + + private void loadPrefs() + { + SQLiteDatabase db = openDB(); + Cursor c = db.query("History", + new String[] { "url", "display", "lastAccessTime" }, + null, null, null, null, "lastAccessTime DESC"); + int urlCol = c.getColumnIndex("url"); + int accessCol = c.getColumnIndex("lastAccessTime"); + int displayCol = c.getColumnIndex("display"); + mHistory.clear(); + while (c.moveToNext()) { + HistoryEntry he = new HistoryEntry(); + he.url = c.getString(urlCol); + he.display = c.getString(displayCol); + he.lastAccessTime = c.getString(accessCol); + mHistory.add(he); + } + + c = db.query("FieldState", null, null, null, null, null, null); + if (c.moveToNext()) { + urlCol = c.getColumnIndex("url"); + displayCol = c.getColumnIndex("display"); + mUrlField.setText(c.getString(urlCol)); + mDisplayField.setText(c.getString(displayCol)); + } else { + mDisplayField.setText("_id"); + mUrlField.setText("content://"); + } + + db.close(); + } + + private void savePrefs() + { + ContentValues m; + HistoryEntry he; + + SQLiteDatabase db = openDB(); + db.execSQL("DELETE FROM History;"); + int count = mHistory.size(); + for (int i=0; i<count; i++) { + m = new ContentValues(); + he = mHistory.get(i); + m.put("url", he.url); + m.put("display", he.display); + m.put("lastAccessTime", he.lastAccessTime); + db.insert("History", null, m); + } + + db.execSQL("DELETE FROM FieldState"); + m = new ContentValues(); + m.put("url", mUrlField.getText().toString()); + m.put("display", mDisplayField.getText().toString()); + db.insert("FieldState", null, m); + + db.close(); + } + + private class HistoryEntry + { + public String url; + public String display; + public String lastAccessTime; + public void updateAccessTime() + { + this.lastAccessTime = DateUtils.writeDateTime( + new GregorianCalendar()); + } + } + + private ArrayList<HistoryEntry> mHistory = new ArrayList<HistoryEntry>(); + private UrlEditText mUrlField; + private DisplayEditText mDisplayField; + private Cursor mCursor; +} diff --git a/apps/Development/src/com/android/development/ExceptionBrowser.java b/apps/Development/src/com/android/development/ExceptionBrowser.java new file mode 100644 index 000000000..63270aab5 --- /dev/null +++ b/apps/Development/src/com/android/development/ExceptionBrowser.java @@ -0,0 +1,171 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.development; + +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.database.ContentObserver; +import android.database.DataSetObserver; +import android.graphics.Typeface; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Checkin; +import android.server.data.CrashData; +import android.server.data.ThrowableData; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.KeyEvent; +import android.widget.*; + +import org.apache.commons.codec.binary.Base64; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * + * + */ +public class ExceptionBrowser extends ListActivity { + /** Logging identifier. */ + private static final String TAG = "ExceptionBrowser"; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + Cursor cursor = getContentResolver().query( + Checkin.Crashes.CONTENT_URI, + new String[] { Checkin.Crashes._ID, Checkin.Crashes.DATA }, + null, null, null); + + if (cursor != null) { + startManagingCursor(cursor); + + setListAdapter(new CursorAdapter(this, cursor, true) { + public View newView(Context context, Cursor c, ViewGroup v) { + return new CrashListItem(context); + } + + public void bindView(View view, Context c, Cursor cursor) { + CrashListItem item = (CrashListItem) view; + try { + String data = cursor.getString(1); + CrashData crash = new CrashData( + new DataInputStream( + new ByteArrayInputStream( + Base64.decodeBase64(data.getBytes())))); + + ThrowableData exc = crash.getThrowableData(); + item.setText(exc.getType() + ": " + exc.getMessage()); + item.setCrashData(crash); + } catch (IOException e) { + item.setText("Invalid crash: " + e); + Log.e(TAG, "Invalid crash", e); + } + } + }); + } else { + // No database, no exceptions, empty list. + setListAdapter(new BaseAdapter() { + public int getCount() { + return 0; + } + + public Object getItem(int position) { + throw new AssertionError(); + } + + public long getItemId(int position) { + throw new AssertionError(); + } + + public View getView(int position, View convertView, + ViewGroup parent) { + throw new AssertionError(); + } + }); + } + } + + private static final int UPLOAD_ID = Menu.FIRST; + private static final int CLEAR_ID = Menu.FIRST + 1; + + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + menu.add(0, UPLOAD_ID, 0, R.string.menu_upload_exceptions); + menu.add(0, CLEAR_ID, 0, R.string.menu_clear_exceptions); + + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle all of the possible menu actions. + switch (item.getItemId()) { + case UPLOAD_ID: + sendBroadcast(new Intent(Checkin.TriggerIntent.ACTION)); + break; + case CLEAR_ID: + getContentResolver().delete( + Checkin.Crashes.CONTENT_URI, null, null); + break; + } + return super.onOptionsItemSelected(item); + } + + static class CrashListItem extends TextView { + CrashData crashData = null; + + public CrashListItem(Context context) { + super(context); + setTextSize(10); + setTypeface(Typeface.MONOSPACE); + } + + public CrashData getCrashData() { + return crashData; + } + + public void setCrashData(CrashData crashData) { + this.crashData = crashData; + } + } + + @Override + protected void onListItemClick(ListView l, View view, int pos, long id) { + // TODO: Use a generic VIEW action on the crash's content URI. + CrashData crash = ((CrashListItem) view).getCrashData(); + if (crash != null) { + Intent intent = new Intent(); + intent.setClass(this, StacktraceViewer.class); + intent.putExtra( + CrashData.class.getName(), + crash.getThrowableData().toString()); + startActivity(intent); + } + } +} diff --git a/apps/Development/src/com/android/development/GLSTester.java b/apps/Development/src/com/android/development/GLSTester.java new file mode 100644 index 000000000..4995b4d04 --- /dev/null +++ b/apps/Development/src/com/android/development/GLSTester.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import com.google.android.googleapps.GoogleLoginCredentialsResult; +import com.google.android.googlelogin.GoogleLoginServiceConstants; +import com.google.android.googleapps.IGoogleLoginService; +import com.google.android.googleapps.LoginData; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.Spinner; + +/** + * Using a LogTextBox to display a scrollable text area + * to which text is appended. + * + */ +public class GLSTester extends Activity { + + private static final String TAG = "GLSTester"; + + private LogTextBox mText; + + private IGoogleLoginService mGls = null; + private ServiceConnection mConnection = null; + + CheckBox mDoNotify = null; + CheckBox mRunIntent = null; + Spinner mService = null; + EditText mUsernameEdit = null; + + private class Listener implements View.OnClickListener { + public Listener() { + } + + public void onClick(View v) { + if (mGls == null) { + mText.append("mGls is null\n"); + return; + } + try { + String service = (String) mService.getSelectedItem(); + mText.append("service: " + service + "\n"); + + String account = mUsernameEdit.getText().toString(); + if (account.length() == 0) { + account = null; + } + mText.append("account: " + account + "\n"); + GoogleLoginCredentialsResult result = + mGls.blockingGetCredentials(account, service, mDoNotify.isChecked()); + mText.append("result account: " + result.getAccount() + "\n"); + mText.append("result string: " + result.getCredentialsString() + "\n"); + Intent intent = result.getCredentialsIntent(); + mText.append("result intent: " + intent + "\n"); + + if (intent != null && mRunIntent.isChecked()) { + startActivityForResult(intent, 0); + } + } catch (RemoteException e) { + mText.append("caught exception " + e + "\n"); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + mText.append("resultCode: " + resultCode + "\n"); + if (data != null) { + mText.append("account: " + + data.getStringExtra(GoogleLoginServiceConstants.AUTH_ACCOUNT_KEY) + "\n"); + mText.append("authtoken: " + + data.getStringExtra(GoogleLoginServiceConstants.AUTHTOKEN_KEY) + "\n"); + } else { + mText.append("intent is null"); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Open a connection to the Google Login Service. Return the account. + mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + mGls = IGoogleLoginService.Stub.asInterface(service); + } + public void onServiceDisconnected(ComponentName className) { + mGls = null; + } + }; + + bindService(GoogleLoginServiceConstants.SERVICE_INTENT, + mConnection, Context.BIND_AUTO_CREATE); + + setContentView(R.layout.gls_tester); + + mText = (LogTextBox) findViewById(R.id.text); + mText.append("Hello, world!\n"); + Log.v(TAG, "hello, world!"); + + mDoNotify = (CheckBox) findViewById(R.id.do_notification); + mRunIntent = (CheckBox) findViewById(R.id.run_intent); + mRunIntent.setChecked(true); + + mService = (Spinner) findViewById(R.id.service_spinner); + + mUsernameEdit = (EditText) findViewById(R.id.username_edit); + + Button b; + b = (Button) findViewById(R.id.require_google); + b.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + try { + String account = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE); + mText.append("REQUIRE_GOOGLE gave: " + account + "\n"); + mUsernameEdit.setText(account == null ? "" : account); + } catch (RemoteException e) { + mText.append("exception: " + e + "\n"); + } + } }); + + b = (Button) findViewById(R.id.prefer_hosted); + b.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + try { + String account = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED); + mText.append("PREFER_HOSTED gave: " + account + "\n"); + mUsernameEdit.setText(account == null ? "" : account); + } catch (RemoteException e) { + mText.append("exception: " + e + "\n"); + } + } }); + + + b = (Button) findViewById(R.id.get_accounts); + b.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + try { + String[] accounts = mGls.getAccounts(); + mText.append("account list: (" + accounts.length + " entries)\n"); + for (String username: accounts) { + mText.append(" " + username + "\n"); + } + } catch (RemoteException e) { + mText.append("exception: " + e + "\n"); + } + } }); + + b = (Button) findViewById(R.id.clear); + b.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + mText.setText(""); + } }); + + b = (Button) findViewById(R.id.go); + b.setOnClickListener(new Listener()); + + b = (Button) findViewById(R.id.wipe_passwords); + b.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + mText.setText("wiping passwords:\n"); + try { + String[] accounts = mGls.getAccounts(); + LoginData ld = new LoginData(); + for (String username: accounts) { + ld.mUsername = username; + mGls.updatePassword(ld); + mText.append(" " + username + "\n"); + } + mText.append("done.\n"); + } catch (RemoteException e) { + mText.append("caught exception " + e + "\n"); + } + } }); + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unbindService(mConnection); + } +} diff --git a/apps/Development/src/com/android/development/IRemoteService.aidl b/apps/Development/src/com/android/development/IRemoteService.aidl new file mode 100644 index 000000000..0a60d3409 --- /dev/null +++ b/apps/Development/src/com/android/development/IRemoteService.aidl @@ -0,0 +1,24 @@ +/* //device/samples/SampleCode/src/com/android/development/IRemoteService.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.development; + +interface IRemoteService +{ + int getPid(); +} + diff --git a/apps/Development/src/com/android/development/InstrumentationList.java b/apps/Development/src/com/android/development/InstrumentationList.java new file mode 100644 index 000000000..88f09f867 --- /dev/null +++ b/apps/Development/src/com/android/development/InstrumentationList.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.ActivityManagerNative; +import android.app.IInstrumentationWatcher; +import android.app.ListActivity; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.Collections; +import java.util.List; + +class InstrumentationAdapter extends BaseAdapter +{ + private PackageManager mPM; + + public InstrumentationAdapter(Context context, String targetPackage) + { + mContext = context; + mTargetPackage = targetPackage; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mPM = context.getPackageManager(); + + mList = context.getPackageManager().queryInstrumentation(mTargetPackage, 0); + if (mList != null) { + Collections.sort(mList, new InstrumentationInfo.DisplayNameComparator(mPM)); + } + } + + public ComponentName instrumentationForPosition(int position) + { + if (mList == null) { + return null; + } + InstrumentationInfo ii = mList.get(position); + return new ComponentName(ii.packageName, ii.name); + } + + public int getCount() + { + return mList != null ? mList.size() : 0; + } + + public Object getItem(int position) + { + return position; + } + + public long getItemId(int position) + { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) + { + View view; + if (convertView == null) { + view = mInflater.inflate( + android.R.layout.simple_list_item_1, parent, false); + } else { + view = convertView; + } + bindView(view, mList.get(position)); + return view; + } + + private final void bindView(View view, InstrumentationInfo info) + { + TextView text = (TextView)view.findViewById(android.R.id.text1); + CharSequence label = info.loadLabel(mPM); + text.setText(label != null ? label : info.name); + } + + protected final Context mContext; + protected final String mTargetPackage; + protected final LayoutInflater mInflater; + + protected List<InstrumentationInfo> mList; +} + +public class InstrumentationList extends ListActivity +{ + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mProfilingMode = icicle != null && icicle.containsKey("profiling"); + setListAdapter(new InstrumentationAdapter(this, null)); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + mProfilingItem = menu.add(0, 0, 0, "Profiling Mode") + .setOnMenuItemClickListener(mProfilingCallback); + mProfilingItem.setCheckable(true); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + super.onPrepareOptionsMenu(menu); + mProfilingItem.setChecked(mProfilingMode); + return true; + } + + @Override + protected void onSaveInstanceState(Bundle outState) + { + if (mProfilingMode) { + outState.putBoolean("profiling", true); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) + { + ComponentName className = ((InstrumentationAdapter)getListAdapter()). + instrumentationForPosition(position); + if (className != null) { + String profilingFile = null; + if (mProfilingMode) { + profilingFile = "/tmp/trace/" + className + ".dmtrace"; + } + try { + ActivityManagerNative.getDefault(). + startInstrumentation(className, profilingFile, 0, null, mWatcher); + } catch (RemoteException ex) { + } + } + } + + private MenuItem.OnMenuItemClickListener mProfilingCallback = + new MenuItem.OnMenuItemClickListener() + { + public boolean onMenuItemClick(MenuItem item) { + mProfilingMode = !mProfilingMode; + return true; + } + }; + + private IInstrumentationWatcher mWatcher = new IInstrumentationWatcher.Stub() { + + public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) { + if (results != null) { + for (String key : results.keySet()) { + Log.i("instrumentation", + "INSTRUMENTATION_STATUS_RESULT: " + key + "=" + results.get(key)); + } + } + Log.i("instrumentation", "INSTRUMENTATION_STATUS_CODE: " + resultCode); + } + public void instrumentationFinished(ComponentName name, + int resultCode, Bundle results) { + if (results != null) { + for (String key : results.keySet()) { + Log.i("instrumentation", + "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key)); + } + } + Log.i("instrumentation", "INSTRUMENTATION_CODE: " + resultCode); + } + }; + + private MenuItem mProfilingItem; + private boolean mProfilingMode; +} diff --git a/apps/Development/src/com/android/development/LogTextBox.java b/apps/Development/src/com/android/development/LogTextBox.java new file mode 100644 index 000000000..2f99ab280 --- /dev/null +++ b/apps/Development/src/com/android/development/LogTextBox.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.widget.TextView; +import android.content.Context; +import android.text.method.ScrollingMovementMethod; +import android.text.method.MovementMethod; +import android.text.method.KeyListener; +import android.text.method.TransformationMethod; +import android.text.Editable; +import android.util.AttributeSet; + + +/** + * This is a TextView that is Editable and by default scrollable, + * like EditText without a cursor. + * + * <p> + * <b>XML attributes</b> + * <p> + * See + * {@link android.R.styleable#TextView TextView Attributes}, + * {@link android.R.styleable#View View Attributes} + */ +public class LogTextBox extends TextView { + public LogTextBox(Context context) { + this(context, null); + } + + public LogTextBox(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.textViewStyle); + } + + public LogTextBox(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected boolean getDefaultEditable() { + return true; + } + + @Override + protected MovementMethod getDefaultMovementMethod() { + return ScrollingMovementMethod.getInstance(); + } + + @Override + public Editable getText() { + return (Editable) super.getText(); + } + + @Override + public void setText(CharSequence text, BufferType type) { + super.setText(text, BufferType.EDITABLE); + } +} diff --git a/apps/Development/src/com/android/development/LogViewer.java b/apps/Development/src/com/android/development/LogViewer.java new file mode 100644 index 000000000..aadc0d423 --- /dev/null +++ b/apps/Development/src/com/android/development/LogViewer.java @@ -0,0 +1,169 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.development; + +import static com.android.internal.util.CharSequences.forAsciiBytes; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.FileOutputStream; +import java.net.Socket; + +import android.app.Activity; +import android.os.Handler; +import android.os.Bundle; +import android.util.Log; +import android.graphics.Typeface; +import android.view.Gravity; + +/** + * Views the device log. + */ +public class LogViewer extends Activity { + + static final String TAG = LogViewer.class.getSimpleName(); + + FileOutputStream logger; + + volatile boolean active = true; + Handler handler; + LogTextBox text; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.log_viewer); + this.handler = new Handler(); + + text = (LogTextBox) findViewById(R.id.text); + + text.setTextSize(10); + text.setHorizontallyScrolling(true); + text.setTypeface(Typeface.MONOSPACE); + text.setGravity(Gravity.BOTTOM | Gravity.LEFT); + + this.active = true; + try { + logger = new FileOutputStream("/tmp/logviewer.txt"); + new Thread(new LogReader()).start(); + } catch (IOException e) { + appendThrowable(e); + } + } + + private void appendThrowable(Throwable t) { + StringBuilder builder = new StringBuilder(); + builder.append("Error reading log: "); + builder.append(Log.getStackTraceString(t)); + text.getText().append(builder); + } + + private class LogReader implements Runnable { + + final Socket socket; + final DataInputStream in; + StringBuilder builder = new StringBuilder(); + long lastTime = System.currentTimeMillis(); + + private static final int HEADER_SIZE = 24; + + public LogReader() throws IOException { + this.socket = new Socket("127.0.0.1", 5040); + this.in = new DataInputStream(this.socket.getInputStream()); + // Write two newlines to indicate "no reader args" + this.socket.getOutputStream().write('\n'); + this.socket.getOutputStream().write('\n'); + } + + public void run() { + while (active) { + try { + while (in.available() > 0) { + logger.write("Reading message.\n".getBytes()); + + int length = in.readInt(); + byte[] bytes = new byte[length]; + in.readFully(bytes); + + int tagEnd = next0(bytes, HEADER_SIZE); + int fileEnd = next0(bytes, tagEnd + 1); + int messageEnd = next0(bytes, fileEnd + 1); + + CharSequence tag + = forAsciiBytes(bytes, HEADER_SIZE, tagEnd); + CharSequence message + = forAsciiBytes(bytes, fileEnd + 1, messageEnd); + + builder.append(tag) + .append(": ") + .append(message) + .append("\n"); + } + + logger.write("Updating UI.\n".getBytes()); + handler.post(new AppendCharacters(builder)); + builder = new StringBuilder(); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) {} + } catch (final IOException e) { + handler.post(new AppendThrowable(e)); + } + } + } + } + + static int next0(byte[] bytes, int start) { + for (int current = start; current < bytes.length; current++) { + if (bytes[current] == 0) + return current; + } + return bytes.length; + } + + private class AppendThrowable implements Runnable { + + private final Throwable t; + + public AppendThrowable(Throwable t) { + this.t = t; + } + + public void run() { + appendThrowable(t); + } + } + + private class AppendCharacters implements Runnable { + + private final CharSequence message; + + public AppendCharacters(CharSequence message) { + this.message = message; + } + + public void run() { + text.getText().append(message); +// try { +// logger.write(builder.toString().getBytes()); +// } catch (IOException e) { +// appendThrowable(e); +// } + } + } +} diff --git a/apps/Development/src/com/android/development/MediaScannerActivity.java b/apps/Development/src/com/android/development/MediaScannerActivity.java new file mode 100644 index 000000000..f78910ac4 --- /dev/null +++ b/apps/Development/src/com/android/development/MediaScannerActivity.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.net.Uri; +import android.os.Environment; +import android.widget.TextView; + +public class MediaScannerActivity extends Activity +{ + public MediaScannerActivity() { + } + + /** Called when the activity is first created or resumed. */ + @Override + public void onResume() { + super.onResume(); + + setContentView(R.layout.media_scanner_activity); + + IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED); + intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); + intentFilter.addDataScheme("file"); + registerReceiver(mReceiver, intentFilter); + + sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + + Environment.getExternalStorageDirectory()))); + + mTitle = (TextView) findViewById(R.id.title); + mTitle.setText("Sent ACTION_MEDIA_MOUNTED to trigger the Media Scanner."); + } + + /** Called when the activity going into the background or being destroyed. */ + @Override + public void onPause() { + super.onPause(); + unregisterReceiver(mReceiver); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) { + mTitle.setText("Media Scanner started scanning " + intent.getData().getPath()); + } + else if (intent.getAction().equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { + mTitle.setText("Media Scanner finished scanning " + intent.getData().getPath()); + } + } + }; + + private TextView mTitle; +} diff --git a/apps/Development/src/com/android/development/PackageBrowser.java b/apps/Development/src/com/android/development/PackageBrowser.java new file mode 100644 index 000000000..e4c233f7d --- /dev/null +++ b/apps/Development/src/com/android/development/PackageBrowser.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.AlertDialog; +import android.app.ListActivity; +import android.content.Context; +import android.content.pm.IPackageDeleteObserver; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.content.pm.PackageInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.Handler; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import android.widget.TextView; + +import java.text.Collator; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class PackageBrowser extends ListActivity +{ + private PackageListAdapter mAdapter; + private List<PackageInfo> mPackageInfoList = null; + private Handler mHandler; + + public class PackageListAdapter extends ArrayAdapter<PackageInfo> + { + + public PackageListAdapter(Context context) + { + super(context, android.R.layout.simple_list_item_1); + mPackageInfoList = context.getPackageManager().getInstalledPackages(0); + if (mPackageInfoList != null) { + Collections.sort(mPackageInfoList, sDisplayNameComparator); + } + setSource(mPackageInfoList); + } + + @Override + public void bindView(View view, PackageInfo info) + { + TextView text = (TextView)view.findViewById(android.R.id.text1); + text.setText(info.packageName); + } + } + + /** + * Receives notifications when applications are added/removed. + */ + private class ApplicationsIntentReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // todo: this is a bit brute force. We should probably get the action and package name + // from the intent and just add to or delete from the mPackageInfoList + setupAdapter(); + } + } + + private final static Comparator sDisplayNameComparator = new Comparator() { + public final int + compare(Object a, Object b) + { + CharSequence sa = ((PackageInfo) a).packageName; + CharSequence sb = ((PackageInfo) b).packageName; + return collator.compare(sa, sb); + } + + private final Collator collator = Collator.getInstance(); + }; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setupAdapter(); + mHandler= new Handler(); + registerIntentReceivers(); + } + + private void setupAdapter() { + mAdapter = new PackageListAdapter(this); + setListAdapter(mAdapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 0, 0, "Delete package").setOnMenuItemClickListener( + new MenuItem.OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + deletePackage(); + return true; + } + }); + return true; + } + + private void deletePackage() { + final int curSelection = getSelectedItemPosition(); + if (curSelection >= 0) { + // todo: verification dialog for package deletion + final PackageInfo packageInfo = mAdapter.itemForPosition(curSelection); + if (packageInfo != null) { + getPackageManager().deletePackage(packageInfo.packageName, + new IPackageDeleteObserver.Stub() { + public void packageDeleted(boolean succeeded) throws RemoteException { + if (succeeded) { + mPackageInfoList.remove(curSelection); + mHandler.post(new Runnable() { + public void run() { + mAdapter.notifyDataSetChanged(); + } + }); + + // todo: verification dialog for data directory + final String dataPath = packageInfo.applicationInfo.dataDir; + // todo: delete the data directory + } else { + mHandler.post(new Runnable() { + public void run() { + new AlertDialog.Builder(PackageBrowser.this) + .setTitle("Oops") + .setMessage("Could not delete package." + + " Maybe it is in /system/app rather than /data/app?") + .show(); + } + }); + + } + } + }, + 0); + } + } + } + + private void registerIntentReceivers() { + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addDataScheme("package"); + registerReceiver(new ApplicationsIntentReceiver(), filter); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) + { + PackageInfo info = + mAdapter.itemForPosition(position); + if (info != null) { + Intent intent = new Intent( + null, Uri.fromParts("package", info.packageName, null)); + intent.setClass(this, PackageSummary.class); + startActivity(intent); + } + } +} diff --git a/apps/Development/src/com/android/development/PackageSummary.java b/apps/Development/src/com/android/development/PackageSummary.java new file mode 100644 index 000000000..a6bbbb227 --- /dev/null +++ b/apps/Development/src/com/android/development/PackageSummary.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.RemoteException; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + + +public class PackageSummary extends Activity { + + String mPackageName; + private TextView mPackage; + private ImageView mIconImage; + private TextView mClass; + private TextView mLabel; + private View mDisabled; + private View mSystem; + private View mDebuggable; + private View mNoCode; + private View mPersistent; + private Button mRestart; + private TextView mTask; + private TextView mVersion; + private TextView mProcess; + private TextView mUid; + private TextView mSource; + private TextView mData; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.package_summary); + + final PackageManager pm = getPackageManager(); + + mPackage = (TextView)findViewById(R.id.packageView); + mIconImage = (ImageView)findViewById(R.id.icon); + mClass = (TextView)findViewById(R.id.classView); + mLabel = (TextView)findViewById(R.id.label); + mDisabled = findViewById(R.id.disabled); + mSystem = findViewById(R.id.system); + mDebuggable = findViewById(R.id.debuggable); + mNoCode = findViewById(R.id.nocode); + mPersistent = findViewById(R.id.persistent); + mRestart = (Button)findViewById(R.id.restart); + mTask = (TextView)findViewById(R.id.task); + mVersion = (TextView)findViewById(R.id.version); + mUid = (TextView)findViewById(R.id.uid); + mProcess = (TextView)findViewById(R.id.process); + mSource = (TextView)findViewById(R.id.source); + mData = (TextView)findViewById(R.id.data); + + mPackageName = getIntent().getData().getSchemeSpecificPart(); + PackageInfo info = null; + try { + info = pm.getPackageInfo(mPackageName, + PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS + | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS + | PackageManager.GET_INSTRUMENTATION); + } catch (PackageManager.NameNotFoundException e) { + } + + if (info != null) { + mPackage.setText(info.packageName); + CharSequence label = null; + String appClass = null; + if (info.applicationInfo != null) { + mIconImage.setImageDrawable( + pm.getApplicationIcon(info.applicationInfo)); + label = info.applicationInfo.nonLocalizedLabel; + appClass = info.applicationInfo.className; + if (info.applicationInfo.enabled) { + mDisabled.setVisibility(View.GONE); + } + if ((info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + mSystem.setVisibility(View.GONE); + } + if ((info.applicationInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + mDebuggable.setVisibility(View.GONE); + } + if ((info.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + mNoCode.setVisibility(View.GONE); + } + if ((info.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { + mPersistent.setVisibility(View.GONE); + } + mUid.setText(Integer.toString(info.applicationInfo.uid)); + mProcess.setText(info.applicationInfo.processName); + if (info.versionName != null) { + mVersion.setText(info.versionName + " (#" + info.versionCode + ")"); + } else { + mVersion.setText("(#" + info.versionCode + ")"); + } + mSource.setText(info.applicationInfo.sourceDir); + mData.setText(info.applicationInfo.dataDir); + if (info.applicationInfo.taskAffinity != null) { + mTask.setText("\"" + info.applicationInfo.taskAffinity + "\""); + } else { + mTask.setText("(No Task Affinity)"); + } + } + if (appClass != null) { + if (appClass.startsWith(info.packageName + ".")) + mClass.setText(appClass.substring(info.packageName.length())); + else + mClass.setText(appClass); + } else { + mClass.setText("(No Application Class)"); + } + if (label != null) { + mLabel.setText("\"" + label + "\""); + } else { + mLabel.setText("(No Label)"); + } + + mRestart.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + try { + ActivityManagerNative.getDefault().restartPackage(mPackageName); + } catch (RemoteException e) { + } + } + }); + + final LayoutInflater inflate = + (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + LinearLayout activities = (LinearLayout)findViewById(R.id.activities); + LinearLayout receivers = (LinearLayout)findViewById(R.id.receivers); + LinearLayout services = (LinearLayout)findViewById(R.id.services); + LinearLayout providers = (LinearLayout)findViewById(R.id.providers); + LinearLayout instrumentation = (LinearLayout)findViewById(R.id.instrumentation); + + if (info.activities != null) { + final int N = info.activities.length; + for (int i=0; i<N; i++) { + ActivityInfo ai = info.activities[i]; + // If an activity is disabled then the ActivityInfo will be null + if (ai != null) { + Button view = (Button)inflate.inflate( + R.layout.package_item, null, false); + view.setOnClickListener(new ActivityOnClick( + new ComponentName(ai.applicationInfo.packageName, + ai.name))); + setItemText(view, info, ai.name); + activities.addView(view, lp); + } + } + } else { + activities.setVisibility(View.GONE); + } + + if (info.receivers != null) { + final int N = info.receivers.length; + for (int i=0; i<N; i++) { + ActivityInfo ai = info.receivers[i]; + Button view = (Button)inflate.inflate( + R.layout.package_item, null, false); + setItemText(view, info, ai.name); + receivers.addView(view, lp); + } + } else { + receivers.setVisibility(View.GONE); + } + + if (info.services != null) { + final int N = info.services.length; + for (int i=0; i<N; i++) { + ServiceInfo si = info.services[i]; + Button view = (Button)inflate.inflate( + R.layout.package_item, null, false); + setItemText(view, info, si.name); + services.addView(view, lp); + } + } else { + services.setVisibility(View.GONE); + } + + if (info.providers != null) { + final int N = info.providers.length; + for (int i=0; i<N; i++) { + ProviderInfo pi = info.providers[i]; + Button view = (Button)inflate.inflate( + R.layout.package_item, null, false); + setItemText(view, info, pi.name); + providers.addView(view, lp); + } + } else { + providers.setVisibility(View.GONE); + } + + if (info.instrumentation != null) { + final int N = info.instrumentation.length; + for (int i=0; i<N; i++) { + InstrumentationInfo ii = info.instrumentation[i]; + Button view = (Button)inflate.inflate( + R.layout.package_item, null, false); + setItemText(view, info, ii.name); + instrumentation.addView(view, lp); + } + } else { + instrumentation.setVisibility(View.GONE); + } + + } + + // Put focus here, so a button doesn't get focus and cause the + // scroll view to move to it. + mPackage.requestFocus(); + } + + private final static void setItemText(Button item, PackageInfo pi, + String className) + { + item.setText(className.substring(pi.packageName.length()+1)); + } + + private final class ActivityOnClick implements View.OnClickListener + { + private final ComponentName mClassName; + ActivityOnClick(ComponentName className) { + mClassName = className; + } + + public void onClick(View v) { + Intent intent = new Intent( + null, Uri.fromParts("component", + mClassName.flattenToString(), null)); + intent.setClass(PackageSummary.this, ShowActivity.class); + startActivity(intent); + } + } +} diff --git a/apps/Development/src/com/android/development/PointerLocation.java b/apps/Development/src/com/android/development/PointerLocation.java new file mode 100644 index 000000000..668e9ba16 --- /dev/null +++ b/apps/Development/src/com/android/development/PointerLocation.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.Paint.FontMetricsInt; +import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.VelocityTracker; +import android.view.View; + +import java.util.ArrayList; + +/** + * Demonstrates wrapping a layout in a ScrollView. + * + */ +public class PointerLocation extends Activity { + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(new MyView(this)); + + // Make the screen full bright for this activity. + WindowManager.LayoutParams lp = getWindow().getAttributes(); + lp.screenBrightness = 1.0f; + getWindow().setAttributes(lp); + } + + public class MyView extends View { + private final Paint mTextPaint; + private final Paint mTextBackgroundPaint; + private final Paint mTextLevelPaint; + private final Paint mPaint; + private final Paint mTargetPaint; + private final FontMetricsInt mTextMetrics = new FontMetricsInt(); + private final ArrayList<Float> mXs = new ArrayList<Float>(); + private final ArrayList<Float> mYs = new ArrayList<Float>(); + private int mHeaderBottom; + private boolean mCurDown; + private int mCurX; + private int mCurY; + private float mCurPressure; + private float mCurSize; + private int mCurWidth; + private VelocityTracker mVelocity; + + public MyView(Context c) { + super(c); + mTextPaint = new Paint(); + mTextPaint.setAntiAlias(true); + mTextPaint.setTextSize(10); + mTextPaint.setARGB(255, 0, 0, 0); + mTextBackgroundPaint = new Paint(); + mTextBackgroundPaint.setAntiAlias(false); + mTextBackgroundPaint.setARGB(128, 255, 255, 255); + mTextLevelPaint = new Paint(); + mTextLevelPaint.setAntiAlias(false); + mTextLevelPaint.setARGB(192, 255, 0, 0); + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setARGB(255, 255, 255, 255); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(2); + mTargetPaint = new Paint(); + mTargetPaint.setAntiAlias(false); + mTargetPaint.setARGB(192, 0, 0, 255); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(1); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mTextPaint.getFontMetricsInt(mTextMetrics); + mHeaderBottom = -mTextMetrics.ascent+mTextMetrics.descent+2; + Log.i("foo", "Metrics: ascent=" + mTextMetrics.ascent + + " descent=" + mTextMetrics.descent + + " leading=" + mTextMetrics.leading + + " top=" + mTextMetrics.top + + " bottom=" + mTextMetrics.bottom); + } + + @Override + protected void onDraw(Canvas canvas) { + int w = getWidth()/5; + int base = -mTextMetrics.ascent+1; + int bottom = mHeaderBottom; + canvas.drawRect(0, 0, w-1, bottom, mTextBackgroundPaint); + canvas.drawText("X: " + mCurX, 1, base, mTextPaint); + canvas.drawRect(w, 0, (w * 2) - 1, bottom, mTextBackgroundPaint); + canvas.drawText("Y: " + mCurY, 1 + w, base, mTextPaint); + canvas.drawRect(w * 2, 0, (w * 3) - 1, bottom, mTextBackgroundPaint); + canvas.drawRect(w * 2, 0, (w * 2) + (mCurPressure * w) - 1, bottom, mTextLevelPaint); + canvas.drawText("Pres: " + mCurPressure, 1 + w * 2, base, mTextPaint); + canvas.drawRect(w * 3, 0, (w * 4) - 1, bottom, mTextBackgroundPaint); + canvas.drawRect(w * 3, 0, (w * 3) + (mCurSize * w) - 1, bottom, mTextLevelPaint); + canvas.drawText("Size: " + mCurSize, 1 + w * 3, base, mTextPaint); + canvas.drawRect(w * 4, 0, getWidth(), bottom, mTextBackgroundPaint); + int velocity = mVelocity == null ? 0 : (int) (mVelocity.getYVelocity() * 1000); + canvas.drawText("yVel: " + velocity, 1 + w * 4, base, mTextPaint); + + final int N = mXs.size(); + float lastX=0, lastY=0; + mPaint.setARGB(255, 0, 255, 255); + for (int i=0; i<N; i++) { + float x = mXs.get(i); + float y = mYs.get(i); + if (i > 0) { + canvas.drawLine(lastX, lastY, x, y, mTargetPaint); + canvas.drawPoint(lastX, lastY, mPaint); + } + lastX = x; + lastY = y; + } + if (mVelocity != null) { + mPaint.setARGB(255, 255, 0, 0); + float xVel = mVelocity.getXVelocity() * (1000/60); + float yVel = mVelocity.getYVelocity() * (1000/60); + canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); + } else { + canvas.drawPoint(lastX, lastY, mPaint); + } + + if (mCurDown) { + canvas.drawLine(0, (int)mCurY, getWidth(), (int)mCurY, mTargetPaint); + canvas.drawLine((int)mCurX, 0, (int)mCurX, getHeight(), mTargetPaint); + int pressureLevel = (int)(mCurPressure*255); + mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel); + canvas.drawPoint(mCurX, mCurY, mPaint); + canvas.drawCircle(mCurX, mCurY, mCurWidth, mPaint); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction(); + //mRect.set(0, 0, getWidth(), mHeaderBottom+1); + //invalidate(mRect); + //if (mCurDown) { + // mRect.set(mCurX-mCurWidth-3, mCurY-mCurWidth-3, + // mCurX+mCurWidth+3, mCurY+mCurWidth+3); + //} else { + // mRect.setEmpty(); + //} + if (action == MotionEvent.ACTION_DOWN) { + mXs.clear(); + mYs.clear(); + mVelocity = VelocityTracker.obtain(); + } + mVelocity.addMovement(event); + mVelocity.computeCurrentVelocity(1); + final int N = event.getHistorySize(); + for (int i=0; i<N; i++) { + mXs.add(event.getHistoricalX(i)); + mYs.add(event.getHistoricalY(i)); + } + mXs.add(event.getX()); + mYs.add(event.getY()); + mCurDown = action == MotionEvent.ACTION_DOWN + || action == MotionEvent.ACTION_MOVE; + mCurX = (int)event.getX(); + mCurY = (int)event.getY(); + mCurPressure = event.getPressure(); + mCurSize = event.getSize(); + mCurWidth = (int)(mCurSize*(getWidth()/3)); + //if (mCurDown) { + // mRect.union(mCurX-mCurWidth-3, mCurY-mCurWidth-3, + // mCurX+mCurWidth+3, mCurY+mCurWidth+3); + //} + //invalidate(mRect); + invalidate(); + return true; + } + + } +} diff --git a/apps/Development/src/com/android/development/ProcessInfo.java b/apps/Development/src/com/android/development/ProcessInfo.java new file mode 100755 index 000000000..5ece1a446 --- /dev/null +++ b/apps/Development/src/com/android/development/ProcessInfo.java @@ -0,0 +1,62 @@ +/* +** +** Copyright 2006, 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.development; + +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class ProcessInfo extends Activity { + PackageManager mPm; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + String processName = intent.getStringExtra("processName"); + String pkgList[] = intent.getStringArrayExtra("packageList"); + mPm = getPackageManager(); + setContentView(R.layout.process_info); + TextView processNameView = (TextView) findViewById(R.id.process_name); + LinearLayout pkgListView = (LinearLayout) findViewById(R.id.package_list); + if(processName != null) { + processNameView.setText(processName); + } + if(pkgList != null) { + for(String pkg : pkgList) { + TextView pkgView = new TextView(this); + pkgView.setText(pkg); + pkgListView.addView(pkgView); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onStop() { + super.onStop(); + } +} + diff --git a/apps/Development/src/com/android/development/RadioIssueReport.java b/apps/Development/src/com/android/development/RadioIssueReport.java new file mode 100644 index 000000000..b1a95f414 --- /dev/null +++ b/apps/Development/src/com/android/development/RadioIssueReport.java @@ -0,0 +1,191 @@ +/* +** Copyright 2006, 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.development; + +import android.app.Activity; +import android.os.Bundle; +import android.os.SystemProperties; +import android.provider.Checkin; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneFactory; +import android.telephony.ServiceState; +import android.text.format.DateFormat; +import static com.android.internal.util.CharSequences.forAsciiBytes; +import android.util.Log; +import android.view.View.OnClickListener; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import com.google.android.collect.Maps; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.net.Socket; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Map; + +/** + * Report radio issues to the StatisticsService. + */ +public class RadioIssueReport extends Activity +{ + private static final String TAG = "RadioIssue"; + private static final int HEADER_SIZE = 24; + private static final String RADIO_BUFFER_OPTIONS = "-b radio\n-d\n"; + + /** List of system properties to snapshot. */ + private static String[] SYSTEM_PROPERTIES = { + "net.gsm.radio-reset", + "net.gsm.attempt-gprs", + "net.gsm.succeed-gprs", + "net.gsm.disconnect", + "net.ppp.sent", + "net.ppp.received", + "gsm.version.baseband", + "gsm.version.ril-impl", + }; + + private Button mSubmitButton; + private EditText mReportText; + private ServiceState mServiceState; + private Phone.State mPhoneState; + private int mSignalStrength; + private Phone.DataState mDataState; + private String mRadioLog; + + /** Snapshot of interesting variables relevant to the radio. */ + private Map<String, String> mRadioState; + + @Override + public void + onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.radio_issue); + + initSubmitButton(); + initReportText(); + + mRadioState = snapState(); + } + + /** + * @return a snapshot of phone state variables to report. + */ + private static Map<String, String> snapState() { + Map<String, String> state = Maps.newHashMap(); + + // Capture a bunch of system properties + for (String property: SYSTEM_PROPERTIES) { + String value = SystemProperties.get(property); + state.put(property, SystemProperties.get(property)); + } + + Phone phone = PhoneFactory.getDefaultPhone(); + state.put("phone-data", phone.getDataConnectionState().toString()); + state.put("phone-service", phone.getServiceState().toString()); + state.put("phone-signal", String.valueOf(phone.getSignalStrengthASU())); + state.put("phone-state", phone.getState().toString()); + + try { + state.put("radio-log", getRadioLog()); + } catch (IOException e) { + Log.e(TAG, "Error reading radio log", e); + } + + return state; + } + + private void initSubmitButton() { + mSubmitButton = (Button) findViewById(R.id.submit); + mSubmitButton.setOnClickListener(mSubmitButtonHandler); + } + + private void initReportText() { + mReportText = (EditText) findViewById(R.id.report_text); + mReportText.requestFocus(); + } + + OnClickListener mSubmitButtonHandler = new OnClickListener() { + public void onClick(View v) { + // Include the user-supplied report text. + mRadioState.put("user-report", mReportText.getText().toString()); + + // Dump the state variables directly into the report. + Checkin.logEvent(getContentResolver(), + Checkin.Events.Tag.RADIO_BUG_REPORT, + mRadioState.toString()); + + finish(); + } + }; + + // Largely stolen from LogViewer.java + private static String getRadioLog() throws IOException { + Socket sock = new Socket("127.0.0.1", 5040); + DataInputStream in = new DataInputStream(sock.getInputStream()); + StringBuilder log = new StringBuilder(); + + // Set options + sock.getOutputStream().write(RADIO_BUFFER_OPTIONS.getBytes()); + sock.getOutputStream().write('\n'); + sock.getOutputStream().write('\n'); + + // Read in the log + try { + Calendar cal = new GregorianCalendar(); + + while (true) { + int length = in.readInt(); + long when = (long)in.readInt(); + byte[] bytes = new byte[length-4]; + in.readFully(bytes); + + int tagEnd = next0(bytes, HEADER_SIZE-4); + int fileEnd = next0(bytes, tagEnd + 1); + int messageEnd = next0(bytes, fileEnd + 1); + + CharSequence tag + = forAsciiBytes(bytes, HEADER_SIZE-4, tagEnd); + CharSequence message + = forAsciiBytes(bytes, fileEnd + 1, messageEnd); + + cal.setTimeInMillis(when*1000); + log.append(DateFormat.format("MM-dd kk:mm:ss ", cal)); + log.append(tag) + .append(": ") + .append(message) + .append("\n"); + } + } catch (EOFException e) { + Log.d(TAG, "reached end of stream"); + } + + return log.toString(); + } + + private static int next0(byte[] bytes, int start) { + for (int current = start; current < bytes.length; current++) { + if (bytes[current] == 0) + return current; + } + return bytes.length; + } +} diff --git a/apps/Development/src/com/android/development/RunningProcesses.java b/apps/Development/src/com/android/development/RunningProcesses.java new file mode 100755 index 000000000..7b56be90b --- /dev/null +++ b/apps/Development/src/com/android/development/RunningProcesses.java @@ -0,0 +1,154 @@ +/* +** +** Copyright 2006, 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.development; + +import android.app.ActivityManager; +import android.app.ListActivity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class RunningProcesses extends ListActivity { + PackageManager mPm; + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mPm = getPackageManager(); + mAdapter = new AppListAdapter(this); + if (mAdapter.getCount() <= 0) { + finish(); + } else { + setListAdapter(mAdapter); + } + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + ListItem app = mAdapter.appForPosition(position); + // Create intent to start new activity + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setClass(this, ProcessInfo.class); + intent.putExtra("processName", app.procInfo.processName); + intent.putExtra("packageList", app.procInfo.pkgList); + // start new activity to display extended information + startActivity(intent); + } + + private final class AppListAdapter extends BaseAdapter { + public AppListAdapter(Context context) { + mContext = context; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + List<ActivityManager.RunningAppProcessInfo> appList = am.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo app : appList) { + if(mList == null) { + mList = new ArrayList<ListItem>(); + } + mList.add(new ListItem(app)); + } + if (mList != null) { + Collections.sort(mList, sDisplayNameComparator); + } + } + + public ListItem appForPosition(int position) { + if (mList == null) { + return null; + } + return mList.get(position); + } + + public int getCount() { + return mList != null ? mList.size() : 0; + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View view; + if (convertView == null) { + view = mInflater.inflate( + android.R.layout.simple_list_item_1, parent, false); + } else { + view = convertView; + } + bindView(view, mList.get(position)); + return view; + } + + private final void bindView(View view, ListItem info) { + TextView text = (TextView)view.findViewById(android.R.id.text1); + text.setText(info != null ? info.procInfo.processName : "(none)"); + } + + protected final Context mContext; + protected final LayoutInflater mInflater; + protected List<ListItem> mList; + + } + + private final Comparator sDisplayNameComparator = new Comparator() { + public final int compare(Object a, Object b) { + CharSequence sa = ((ListItem) a).procInfo.processName; + CharSequence sb = ((ListItem) b).procInfo.processName; + return collator.compare(sa, sb); + } + private final Collator collator = Collator.getInstance(); + }; + + private class ListItem { + ActivityManager.RunningAppProcessInfo procInfo; + public ListItem(ActivityManager.RunningAppProcessInfo pInfo) { + procInfo = pInfo; + } + } + + private AppListAdapter mAdapter; +} + diff --git a/apps/Development/src/com/android/development/ShowActivity.java b/apps/Development/src/com/android/development/ShowActivity.java new file mode 100644 index 000000000..1fc281640 --- /dev/null +++ b/apps/Development/src/com/android/development/ShowActivity.java @@ -0,0 +1,122 @@ +/* //device/apps/Settings/src/com/android/settings/Keyguard.java +** +** Copyright 2006, 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.development; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.provider.Settings; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + + +public class ShowActivity extends Activity { + + private ActivityInfo mActivityInfo; + + private TextView mPackage; + private ImageView mIconImage; + private TextView mClass; + private TextView mLabel; + private TextView mLaunch; + private TextView mProcess; + private TextView mTaskAffinity; + private TextView mPermission; + private TextView mMultiprocess; + private TextView mClearOnBackground; + private TextView mStateNotNeeded; + + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + setContentView(R.layout.show_activity); + + mPackage = (TextView)findViewById(R.id.packageView); + mIconImage = (ImageView)findViewById(R.id.icon); + mClass = (TextView)findViewById(R.id.classView); + mLabel = (TextView)findViewById(R.id.label); + mLaunch = (TextView)findViewById(R.id.launch); + mProcess = (TextView)findViewById(R.id.process); + mTaskAffinity = (TextView)findViewById(R.id.taskAffinity); + mPermission = (TextView)findViewById(R.id.permission); + mMultiprocess = (TextView)findViewById(R.id.multiprocess); + mClearOnBackground = (TextView)findViewById(R.id.clearOnBackground); + mStateNotNeeded = (TextView)findViewById(R.id.stateNotNeeded); + + final PackageManager pm = getPackageManager(); + try { + mActivityInfo = pm.getActivityInfo(ComponentName.unflattenFromString( + getIntent().getData().getSchemeSpecificPart()), 0); + } catch (PackageManager.NameNotFoundException e) { + } + if (mActivityInfo != null) { + mPackage.setText(mActivityInfo.applicationInfo.packageName); + mIconImage.setImageDrawable(mActivityInfo.loadIcon(pm)); + if (mActivityInfo.name.startsWith( + mActivityInfo.applicationInfo.packageName + ".")) { + mClass.setText(mActivityInfo.name.substring( + mActivityInfo.applicationInfo.packageName.length())); + } else { + mClass.setText(mActivityInfo.name); + } + CharSequence label = mActivityInfo.loadLabel(pm); + mLabel.setText("\"" + (label != null ? label : "") + "\""); + switch (mActivityInfo.launchMode) { + case ActivityInfo.LAUNCH_MULTIPLE: + mLaunch.setText(getText(R.string.launch_multiple)); + break; + case ActivityInfo.LAUNCH_SINGLE_TOP: + mLaunch.setText(getText(R.string.launch_singleTop)); + break; + case ActivityInfo.LAUNCH_SINGLE_TASK: + mLaunch.setText(getText(R.string.launch_singleTask)); + break; + case ActivityInfo.LAUNCH_SINGLE_INSTANCE: + mLaunch.setText(getText(R.string.launch_singleInstance)); + break; + default: + mLaunch.setText(getText(R.string.launch_unknown)); + } + mProcess.setText(mActivityInfo.processName); + mTaskAffinity.setText(mActivityInfo.taskAffinity != null + ? mActivityInfo.taskAffinity : getText(R.string.none)); + mPermission.setText(mActivityInfo.permission != null + ? mActivityInfo.permission : getText(R.string.none)); + mMultiprocess.setText( + (mActivityInfo.flags&ActivityInfo.FLAG_MULTIPROCESS) != 0 + ? getText(R.string.yes) : getText(R.string.no)); + mClearOnBackground.setText( + (mActivityInfo.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0 + ? getText(R.string.yes) : getText(R.string.no)); + mStateNotNeeded.setText( + (mActivityInfo.flags&ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0 + ? getText(R.string.yes) : getText(R.string.no)); + } + } + + public void onResume() { + super.onResume(); + } +} diff --git a/apps/Development/src/com/android/development/StacktraceViewer.java b/apps/Development/src/com/android/development/StacktraceViewer.java new file mode 100644 index 000000000..96b052129 --- /dev/null +++ b/apps/Development/src/com/android/development/StacktraceViewer.java @@ -0,0 +1,50 @@ +/* +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.development; + + +import android.app.Activity; +import android.widget.EditText; +import android.widget.TextView; +import android.text.method.ScrollingMovementMethod; +import android.os.Bundle; +import android.server.data.CrashData; +import android.server.data.ThrowableData; +import android.server.data.StackTraceElementData; +import android.graphics.Typeface; + +/** + * Views a single stack trace. + */ +public class StacktraceViewer extends Activity { + + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.log_viewer); + + TextView text = (TextView) findViewById(R.id.text); + text.setTextSize(10); + text.setHorizontallyScrolling(true); + text.setTypeface(Typeface.MONOSPACE); + text.setMovementMethod(ScrollingMovementMethod.getInstance()); + + String stacktrace = getIntent().getExtras().getString( + CrashData.class.getName()); + + text.setText(stacktrace); + } +} diff --git a/apps/Development/src/com/android/development/UnderdevelopedSettings.java b/apps/Development/src/com/android/development/UnderdevelopedSettings.java new file mode 100644 index 000000000..9cf1b9085 --- /dev/null +++ b/apps/Development/src/com/android/development/UnderdevelopedSettings.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.development; + +import android.app.LauncherActivity; +import android.content.Intent; + +public class UnderdevelopedSettings extends LauncherActivity { + + @Override + protected Intent getTargetIntent() { + Intent targetIntent = new Intent(Intent.ACTION_MAIN, null); + targetIntent.addCategory(Intent.CATEGORY_DEVELOPMENT_PREFERENCE); + return targetIntent; + } + +} |
