Java tutorial
/* Copyright (c) 2013 Dattas Moonchaser Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.dattasmoon.pebble.plugin; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; import android.view.*; import android.view.View.OnClickListener; import android.widget.*; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.CompoundButton.OnCheckedChangeListener; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class EditNotificationActivity extends AbstractPluginActivity { ListView lvPackages; TextView tvTaskerNotice; Constants.Mode mMode; Spinner spMode; SharedPreferences sharedPreferences; Handler mHandler; JSONArray arrayRenames; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_edit_notification); mHandler = new Handler(); lvPackages = (ListView) findViewById(R.id.listPackages); spMode = (Spinner) findViewById(R.id.spMode); tvTaskerNotice = (TextView) findViewById(R.id.tvTaskerNotice); findViewById(R.id.tvAccessibilityError).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); } }); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.mode_choices, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spMode.setAdapter(adapter); spMode.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { mMode = Constants.Mode.values()[pos]; } @Override public void onNothingSelected(AdapterView<?> parent) { mMode = Constants.Mode.OFF; if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Mode is: off"); } } }); } @Override public void onResume() { super.onResume(); if (mode == Mode.STANDARD) { sharedPreferences = getSharedPreferences(Constants.LOG_TAG + "_preferences", MODE_MULTI_PROCESS | MODE_PRIVATE); if (sharedPreferences.getBoolean(Constants.PREFERENCE_TASKER_SET, false)) { tvTaskerNotice.setVisibility(View.VISIBLE); } spMode.setSelection(sharedPreferences.getInt(Constants.PREFERENCE_MODE, Constants.Mode.OFF.ordinal())); // legacy preference handler if (sharedPreferences.contains(Constants.PREFERENCE_EXCLUDE_MODE)) { if (sharedPreferences.getBoolean(Constants.PREFERENCE_EXCLUDE_MODE, false)) { spMode.setSelection(Constants.Mode.INCLUDE.ordinal()); } else { spMode.setSelection(Constants.Mode.EXCLUDE.ordinal()); } } } else if (mode == Mode.LOCALE) { if (localeBundle != null) { spMode.setSelection( localeBundle.getInt(Constants.BUNDLE_EXTRA_INT_MODE, Constants.Mode.OFF.ordinal())); } } checkAccessibilityService(); if (findViewById(R.id.listPackages).isEnabled()) { new LoadAppsTask().execute(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.activity_edit_notifications, menu); if (mode == Mode.LOCALE) { menu.removeItem(R.id.btnSettings); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Selected menu item id: " + String.valueOf(item.getItemId())); } final AlertDialog.Builder builder = new AlertDialog.Builder(this); View v; ListViewHolder viewHolder; AdapterView.AdapterContextMenuInfo contextInfo; String app_name; final String package_name; switch (item.getItemId()) { case R.id.btnUncheckAll: builder.setTitle(R.string.dialog_confirm_title); builder.setMessage(getString(R.string.dialog_uncheck_message)); builder.setCancelable(false); builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int buttonId) { if (lvPackages == null || lvPackages.getAdapter() == null || ((packageAdapter) lvPackages.getAdapter()).selected == null) { //something went wrong return; } ((packageAdapter) lvPackages.getAdapter()).selected.clear(); lvPackages.invalidateViews(); } }); builder.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int buttonId) { //do nothing! } }); builder.setIcon(android.R.drawable.ic_dialog_alert); builder.show(); return true; case R.id.btnSave: finish(); return true; case R.id.btnDonate: Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(Constants.DONATION_URL)); startActivity(i); return true; case R.id.btnSettings: Intent settings = new Intent(this, SettingsActivity.class); startActivity(settings); return true; case R.id.btnRename: contextInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); int position = contextInfo.position; long id = contextInfo.id; // the child view who's info we're viewing (should be equal to v) v = contextInfo.targetView; app_name = ((TextView) v.findViewById(R.id.tvPackage)).getText().toString(); viewHolder = (ListViewHolder) v.getTag(); if (viewHolder == null || viewHolder.chkEnabled == null) { //failure return true; } package_name = (String) viewHolder.chkEnabled.getTag(); builder.setTitle(R.string.dialog_title_rename_notification); final EditText input = new EditText(this); input.setHint(app_name); builder.setView(input); builder.setPositiveButton(R.string.confirm, null); builder.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //do nothing } }); final AlertDialog d = builder.create(); d.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { Button b = d.getButton(AlertDialog.BUTTON_POSITIVE); if (b != null) { b.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //can't be nothing if (input.getText().length() > 0) { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Adding rename for " + package_name + " to " + input.getText()); } JSONObject rename = new JSONObject(); try { rename.put("pkg", package_name); rename.put("to", input.getText()); arrayRenames.put(rename); } catch (JSONException e) { e.printStackTrace(); } ((packageAdapter) lvPackages.getAdapter()).notifyDataSetChanged(); d.dismiss(); } else { input.setText(R.string.error_cant_be_blank); } } }); } } }); d.show(); return true; case R.id.btnRemoveRename: contextInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); // the child view who's info we're viewing (should be equal to v) v = contextInfo.targetView; app_name = ((TextView) v.findViewById(R.id.tvPackage)).getText().toString(); viewHolder = (ListViewHolder) v.getTag(); if (viewHolder == null || viewHolder.chkEnabled == null) { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Viewholder is null or chkEnabled is null"); } //failure return true; } package_name = (String) viewHolder.chkEnabled.getTag(); builder.setTitle( getString(R.string.dialog_title_remove_rename) + app_name + " (" + package_name + ")?"); builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Before remove is: " + String.valueOf(arrayRenames.length())); } JSONArray tmp = new JSONArray(); try { for (int i = 0; i < arrayRenames.length(); i++) { if (!arrayRenames.getJSONObject(i).getString("pkg").equalsIgnoreCase(package_name)) { tmp.put(arrayRenames.getJSONObject(i)); } } } catch (JSONException e) { e.printStackTrace(); } arrayRenames = tmp; if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "After remove is: " + String.valueOf(arrayRenames.length())); } ((packageAdapter) lvPackages.getAdapter()).notifyDataSetChanged(); } }); builder.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //do nothing } }); builder.show(); return true; } return super.onOptionsItemSelected(item); } @Override public boolean onMenuItemSelected(final int featureId, final MenuItem item) { return onOptionsItemSelected(item); } public void checkAccessibilityService() { int accessibilityEnabled = 0; boolean accessibilityFound = false; try { accessibilityEnabled = Settings.Secure.getInt(this.getContentResolver(), android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); } catch (SettingNotFoundException e) { } TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); if (accessibilityEnabled == 1) { String settingValue = Settings.Secure.getString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); if (settingValue != null) { TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; splitter.setString(settingValue); while (splitter.hasNext()) { String accessabilityService = splitter.next(); if (accessabilityService.equalsIgnoreCase(Constants.ACCESSIBILITY_SERVICE)) { accessibilityFound = true; break; } } } } if (!accessibilityFound) { findViewById(R.id.tvAccessibilityError).setVisibility(View.VISIBLE); findViewById(R.id.spMode).setVisibility(View.GONE); findViewById(R.id.tvMode).setVisibility(View.GONE); findViewById(android.R.id.empty).setVisibility(View.GONE); findViewById(R.id.listPackages).setEnabled(false); if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "The accessibility service is NOT on!"); } } else { findViewById(R.id.tvAccessibilityError).setVisibility(View.GONE); findViewById(R.id.spMode).setVisibility(View.VISIBLE); findViewById(R.id.tvMode).setVisibility(View.VISIBLE); findViewById(android.R.id.empty).setVisibility(View.VISIBLE); findViewById(R.id.listPackages).setEnabled(true); if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "The accessibility service is on!"); } } } @Override protected void onSaveInstanceState(Bundle outState) { save(); super.onSaveInstanceState(outState); } public void save() { String selectedPackages = ""; ArrayList<String> tmpArray = new ArrayList<String>(); if (lvPackages == null || lvPackages.getAdapter() == null) { return; } for (String strPackage : ((packageAdapter) lvPackages.getAdapter()).selected) { if (!strPackage.isEmpty()) { if (!tmpArray.contains(strPackage)) { tmpArray.add(strPackage); selectedPackages += strPackage + ","; } } } tmpArray.clear(); tmpArray = null; if (!selectedPackages.isEmpty()) { selectedPackages = selectedPackages.substring(0, selectedPackages.length() - 1); } if (Constants.IS_LOGGABLE) { switch (mMode) { case OFF: Log.i(Constants.LOG_TAG, "Mode is: off"); break; case EXCLUDE: Log.i(Constants.LOG_TAG, "Mode is: exclude"); break; case INCLUDE: Log.i(Constants.LOG_TAG, "Mode is: include"); break; } Log.i(Constants.LOG_TAG, "Package list is: " + selectedPackages); } if (mode == Mode.STANDARD) { Editor editor = sharedPreferences.edit(); editor.putInt(Constants.PREFERENCE_MODE, mMode.ordinal()); editor.putString(Constants.PREFERENCE_PACKAGE_LIST, selectedPackages); editor.putString(Constants.PREFERENCE_PKG_RENAMES, arrayRenames.toString()); // we saved via the application, reset the variable if it exists editor.remove(Constants.PREFERENCE_TASKER_SET); // clear out legacy preference, if it exists editor.remove(Constants.PREFERENCE_EXCLUDE_MODE); // save! editor.commit(); // notify service via file that it needs to reload the preferences File watchFile = new File(getFilesDir() + "PrefsChanged.none"); if (!watchFile.exists()) { try { watchFile.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } watchFile.setLastModified(System.currentTimeMillis()); } else if (mode == Mode.LOCALE) { if (!isCanceled()) { final Intent resultIntent = new Intent(); final Bundle resultBundle = new Bundle(); // set the version, title and body resultBundle.putInt(Constants.BUNDLE_EXTRA_INT_VERSION_CODE, Constants.getVersionCode(getApplicationContext())); resultBundle.putInt(Constants.BUNDLE_EXTRA_INT_TYPE, Constants.Type.SETTINGS.ordinal()); resultBundle.putInt(Constants.BUNDLE_EXTRA_INT_MODE, mMode.ordinal()); resultBundle.putString(Constants.BUNDLE_EXTRA_STRING_PACKAGE_LIST, selectedPackages); resultBundle.putString(Constants.BUNDLE_EXTRA_STRING_PKG_RENAMES, arrayRenames.toString()); String blurb = ""; switch (mMode) { case OFF: blurb = getResources().getStringArray(R.array.mode_choices)[0]; break; case INCLUDE: blurb = getResources().getStringArray(R.array.mode_choices)[2]; break; case EXCLUDE: blurb = getResources().getStringArray(R.array.mode_choices)[1]; } Log.i(Constants.LOG_TAG, resultBundle.toString()); resultIntent.putExtra(com.twofortyfouram.locale.Intent.EXTRA_BUNDLE, resultBundle); resultIntent.putExtra(com.twofortyfouram.locale.Intent.EXTRA_STRING_BLURB, blurb); setResult(RESULT_OK, resultIntent); } } } @Override public void finish() { if (!lvPackages.isEnabled()) { // if the list is not enabled, we don't want to save settings super.finish(); return; } save(); super.finish(); } private class LoadAppsTask extends AsyncTask<Void, Integer, Void> { public ArrayList<String> selected; List<PackageInfo> pkgAppsList; List<ApplicationInfo> appsList; JSONArray jsonRenames; @Override protected void onPreExecute() { PackageManager pm = getPackageManager(); try { pkgAppsList = pm.getInstalledPackages(0); } catch (RuntimeException e) { //this is usually thrown when people have too many things installed (or bloatware in the case of Samsung devices) pm = getPackageManager(); appsList = pm.getInstalledApplications(0); } } @Override protected Void doInBackground(Void... unused) { if (pkgAppsList == null && appsList == null) { //something went really bad here return null; } if (appsList == null) { appsList = new ArrayList<ApplicationInfo>(); for (PackageInfo pkg : pkgAppsList) { appsList.add(pkg.applicationInfo); } } AppComparator comparer = new AppComparator(EditNotificationActivity.this); Collections.sort(appsList, comparer); selected = new ArrayList<String>(); String packageList; String packageRenames; if (mode == Mode.LOCALE) { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Locale mode"); } if (localeBundle != null) { packageRenames = localeBundle.getString(Constants.BUNDLE_EXTRA_STRING_PKG_RENAMES); packageList = localeBundle.getString(Constants.BUNDLE_EXTRA_STRING_PACKAGE_LIST); if (packageList == null) { // this can be null if it doesn't currently exist in the // locale bundle, handle gracefully packageList = ""; if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Package list from locale bundle is currently null"); } } if (packageRenames == null) { packageRenames = "[]"; } } else { packageList = ""; packageRenames = "[]"; if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Locale bundle is null"); } } } else { if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "I am pulling from sharedPrefs"); } packageList = sharedPreferences.getString(Constants.PREFERENCE_PACKAGE_LIST, ""); packageRenames = sharedPreferences.getString(Constants.PREFERENCE_PKG_RENAMES, "[]"); } if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Package list is: " + packageList); } for (String strPackage : packageList.split(",")) { // only add the ones that are still installed, providing cleanup // and faster speeds all in one! for (ApplicationInfo info : appsList) { if (info.packageName.equalsIgnoreCase(strPackage)) { selected.add(strPackage); } } } try { jsonRenames = new JSONArray(packageRenames); } catch (JSONException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void unused) { if (appsList == null) { //something went wrong return; } if (jsonRenames == null) { arrayRenames = new JSONArray(); } else { arrayRenames = jsonRenames; } findViewById(android.R.id.empty).setVisibility(View.GONE); lvPackages.setAdapter(new packageAdapter(EditNotificationActivity.this, appsList.toArray(new ApplicationInfo[appsList.size()]), selected)); lvPackages.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { AdapterView.AdapterContextMenuInfo contextInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; int position = contextInfo.position; long id = contextInfo.id; // the child view who's info we're viewing (should be equal to v) View v = contextInfo.targetView; MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.list_application_menu, menu); ListViewHolder viewHolder = (ListViewHolder) v.getTag(); if (viewHolder.renamed) { menu.findItem(R.id.btnRename).setVisible(false); menu.findItem(R.id.btnRemoveRename).setVisible(true); } } }); } } private class packageAdapter extends ArrayAdapter<ApplicationInfo> implements OnCheckedChangeListener, OnClickListener { private final Context context; private final PackageManager pm; private final ApplicationInfo[] packages; public ArrayList<String> selected; public packageAdapter(Context context, ApplicationInfo[] packages, ArrayList<String> selected) { super(context, R.layout.list_application_item, packages); this.context = context; this.pm = context.getPackageManager(); this.packages = packages; this.selected = selected; } @Override public View getView(int position, View rowView, ViewGroup parent) { ListViewHolder viewHolder = null; if (rowView == null) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); rowView = inflater.inflate(R.layout.list_application_item, null, false); viewHolder = new ListViewHolder(); viewHolder.textView = (TextView) rowView.findViewById(R.id.tvPackage); viewHolder.imageView = (ImageView) rowView.findViewById(R.id.ivIcon); viewHolder.chkEnabled = (CheckBox) rowView.findViewById(R.id.chkEnabled); viewHolder.chkEnabled.setOnCheckedChangeListener(this); rowView.setOnClickListener(this); //really wish we didn't have to do this, but if we don't the rowview will gobble this event up. rowView.setOnCreateContextMenuListener(null); rowView.setTag(viewHolder); } else { viewHolder = (ListViewHolder) rowView.getTag(); } ApplicationInfo info = packages[position]; String appName = null; viewHolder.renamed = false; try { for (int i = 0; i < arrayRenames.length(); i++) { if (arrayRenames.getJSONObject(i).getString("pkg").equalsIgnoreCase(info.packageName)) { viewHolder.renamed = true; appName = arrayRenames.getJSONObject(i).getString("to"); viewHolder.textView.setTag(appName); break; } } if (!viewHolder.renamed) { appName = info.loadLabel(pm).toString(); } } catch (NullPointerException e) { appName = null; } catch (JSONException e) { appName = null; } if (appName != null) { viewHolder.textView.setText(appName); } else { viewHolder.textView.setText(""); } Drawable icon; try { icon = info.loadIcon(pm); } catch (NullPointerException e) { icon = null; } if (icon != null) { viewHolder.imageView.setImageDrawable(icon); } viewHolder.chkEnabled.setTag(info.packageName); boolean boolSelected = false; for (String strPackage : selected) { if (info.packageName.equalsIgnoreCase(strPackage)) { boolSelected = true; break; } } viewHolder.chkEnabled.setChecked(boolSelected); return rowView; } @Override public void onCheckedChanged(CompoundButton chkEnabled, boolean newState) { String strPackage = (String) chkEnabled.getTag(); if (strPackage.isEmpty()) { return; } if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Check changed on " + strPackage); } if (newState) { if (!selected.contains(strPackage)) { selected.add(strPackage); } } else { while (selected.contains(strPackage)) { selected.remove(strPackage); } } if (Constants.IS_LOGGABLE) { Log.i(Constants.LOG_TAG, "Selected count is: " + String.valueOf(selected.size())); } } @Override public void onClick(View rowView) { ((CheckBox) rowView.findViewById(R.id.chkEnabled)).performClick(); } } public static class ListViewHolder { public boolean renamed; public TextView textView; public CheckBox chkEnabled; public ImageView imageView; public ListViewHolder() { renamed = false; } } public class AppComparator implements Comparator<ApplicationInfo> { final PackageManager pm; public AppComparator(Context context) { this.pm = context.getPackageManager(); } @Override public int compare(ApplicationInfo leftPackage, ApplicationInfo rightPackage) { String leftName = leftPackage.loadLabel(pm).toString(); String rightName = rightPackage.loadLabel(pm).toString(); return leftName.compareToIgnoreCase(rightName); } } }