/******************************************************************************* * Copyright (c) 2011 Adam Shanks (ChainsDD) * * 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.noshufou.android.su; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import com.noshufou.android.su.preferences.Preferences; import com.noshufou.android.su.provider.PermissionsProvider.Apps; import com.noshufou.android.su.provider.PermissionsProvider.Logs; import com.noshufou.android.su.service.ResultService; import com.noshufou.android.su.util.Util; import com.noshufou.android.su.widget.BetterPopupWindow; import com.noshufou.android.su.widget.LogAdapter; import com.noshufou.android.su.widget.PinnedHeaderListView; public class AppDetailsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>, FragmentWithLog, OnClickListener { // private static final String TAG = "Su.AppDetailsFragment"; private TextView mAppName = null; private ImageView mAppIcon = null; private ImageView mStatusIcon = null; private LinearLayout mDetailsContainer = null; private TextView mPackageNameText = null; private TextView mAppUidText = null; private TextView mRequestDetailText = null; private TextView mCommandText = null; private TextView mStatusText = null; private boolean mUseAppSettings = true; private boolean mNotificationsEnabled = true; private boolean mLoggingEnabled = true; private Button mToggleButton = null; private static final int DETAILS_LOADER = 1; private static final int LOG_LOADER = 2; private long mShownIndex = -1; private boolean mReady = false; private boolean mDualPane = false; private int mAllow = -1; LogAdapter mAdapter = null; public static final String[] DETAILS_PROJECTION = new String[] { Apps._ID, Apps.UID, Apps.PACKAGE, Apps.NAME, Apps.EXEC_UID, Apps.EXEC_CMD, Apps.ALLOW, Apps.NOTIFICATIONS, Apps.LOGGING }; private static final int DETAILS_COLUMN_UID = 1; private static final int DETAILS_COLUMN_PACKAGE = 2; private static final int DETAILS_COLUMN_NAME = 3; private static final int DETAILS_COLUMN_EXEC_UID = 4; private static final int DETAILS_COLUMN_EXEC_CMD = 5; private static final int DETAILS_COLUMN_ALLOW = 6; private static final int DETAILS_COLUMN_NOTIFICATIONS = 7; private static final int DETAILS_COLUMN_LOGGING = 8; public static AppDetailsFragment newInstance(long index) { AppDetailsFragment fragment = new AppDetailsFragment(); Bundle args = new Bundle(); args.putLong("index", index); fragment.setArguments(args); return fragment; } public long getShownIndex() { return mShownIndex; } public void setShownIndex(long index) { mShownIndex = index; getLoaderManager().restartLoader(DETAILS_LOADER, null, this); getLoaderManager().restartLoader(LOG_LOADER, null, this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { return null; } View view = inflater.inflate(R.layout.fragment_app_details, container, false); mAppName = (TextView) view.findViewById(R.id.app_name); mAppIcon = (ImageView) view.findViewById(R.id.app_icon); mStatusIcon = (ImageView) view.findViewById(R.id.status_icon); mDetailsContainer = (LinearLayout) view.findViewById(R.id.details_container); mPackageNameText = (TextView) view.findViewById(R.id.package_name); mAppUidText = (TextView) view.findViewById(R.id.app_uid); mRequestDetailText = (TextView) view.findViewById(R.id.request_detail); mCommandText = (TextView) view.findViewById(R.id.command); mStatusText = (TextView) view.findViewById(R.id.status); view.findViewById(R.id.toggle_button).setOnClickListener(this); view.findViewById(R.id.forget_button).setOnClickListener(this); view.findViewById(R.id.clear_log_button).setOnClickListener(this); if (Util.elitePresent(getActivity(), true, 2)) { View moreButton = view.findViewById(R.id.more_button); moreButton.setOnClickListener(this); moreButton.setVisibility(View.VISIBLE); } View closeButton = view.findViewById(R.id.close_button); if (closeButton != null) { closeButton.setOnClickListener(this); } return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); FrameLayout fragmentContainer = (FrameLayout) getActivity() .findViewById(R.id.fragment_container); if (fragmentContainer != null) { mDualPane = true; } if (savedInstanceState != null && savedInstanceState.containsKey("mShownIndex")) { mShownIndex = savedInstanceState.getLong("mShownIndex"); } else if (getArguments() != null) { mShownIndex = getArguments().getLong("index", 0); } else { mShownIndex = 0; } setupListView(); getLoaderManager().initLoader(DETAILS_LOADER, null, this); getLoaderManager().initLoader(LOG_LOADER, null, this); } private void setupListView() { final ListView list = getListView(); final LayoutInflater inflater = getActivity().getLayoutInflater(); list.setDividerHeight(0); mAdapter = new LogAdapter(null, getActivity(), false); setListAdapter(mAdapter); if (list instanceof PinnedHeaderListView && mAdapter.getDisplaySectionHeadersEnabled()) { PinnedHeaderListView pinnedHeaderListView = (PinnedHeaderListView) list; View pinnedHeader = inflater.inflate(R.layout.recent_list_section, list, false); pinnedHeaderListView.setPinnedHeaderView(pinnedHeader); } list.setOnScrollListener(mAdapter); } @Override public void onSaveInstanceState(Bundle outState) { if (mShownIndex != -1) { outState.putLong("mShownIndex", mShownIndex); } super.onSaveInstanceState(outState); } @Override public void onClick(View view) { switch(view.getId()) { case R.id.toggle_button: toggle(null); break; case R.id.forget_button: forget(null); break; case R.id.clear_log_button: clearLog(); break; case R.id.more_button: MoreOptionsPopup popup = new MoreOptionsPopup(view); popup.show(); break; case R.id.close_button: ((HomeActivity)getActivity()).closeDetails(); break; } } public void toggle(View view) { if (!mReady) { return; } if (PreferenceManager.getDefaultSharedPreferences(getActivity()) .getBoolean(Preferences.PIN, false)) { Intent intent = new Intent(getActivity(), PinActivity.class); intent.putExtra(PinActivity.EXTRA_MODE, PinActivity.MODE_CHECK); startActivityForResult(intent, 0); } else { doToggle(); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { doToggle(); } } private void doToggle() { ContentResolver cr = getActivity().getContentResolver(); Uri uri = Uri.withAppendedPath(Apps.CONTENT_URI, String.valueOf(mShownIndex)); ContentValues values = new ContentValues(); values.put(Apps.ALLOW, mAllow == 1?0:1); cr.update(uri, values, null, null); // Update the log values.clear(); values.put(Logs.DATE, System.currentTimeMillis()); values.put(Logs.TYPE, Logs.LogType.TOGGLE); cr.insert(Uri.withAppendedPath(Logs.CONTENT_URI, String.valueOf(mShownIndex)), values); Intent intent = new Intent(getActivity(), ResultService.class); intent.putExtra(ResultService.EXTRA_ACTION, ResultService.ACTION_RECYCLE); getActivity().startService(intent); } public void forget(View view) { if (!mReady) { return; } ContentResolver cr = getActivity().getContentResolver(); Uri uri = Uri.withAppendedPath(Apps.CONTENT_URI, String.valueOf(mShownIndex)); cr.delete(uri, null, null); closeDetails(); } public void clearLog(View view) { clearLog(); } @Override public void clearLog() { if (mShownIndex != -1) { getActivity().getContentResolver().delete(ContentUris.withAppendedId(Logs.CONTENT_URI, mShownIndex), null, null); } } public void closeDetails() { if (mDualPane) { Fragment logFragment = LogFragment.newInstance(); FragmentTransaction transaction = ((FragmentActivity)getActivity()) .getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.fragment_container, logFragment); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); transaction.commit(); } else { Util.goHome(getActivity()); } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { switch (id) { case DETAILS_LOADER: return new CursorLoader(getActivity(), ContentUris.withAppendedId(Apps.CONTENT_URI, mShownIndex), DETAILS_PROJECTION, null, null, null); case LOG_LOADER: return new CursorLoader(getActivity(), ContentUris.withAppendedId(Logs.CONTENT_URI, mShownIndex), LogAdapter.PROJECTION, null, null, null); default: throw new IllegalArgumentException("Unknown Loader: " + id); } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { switch (loader.getId()) { case DETAILS_LOADER: if (data.moveToFirst()) { if (mDetailsContainer != null) { mDetailsContainer.setVisibility(View.VISIBLE); } mAppName.setText(data.getString(DETAILS_COLUMN_NAME)); mAppIcon.setImageDrawable( Util.getAppIcon(getActivity(), data.getInt(DETAILS_COLUMN_UID))); int allow = data.getInt(DETAILS_COLUMN_ALLOW); mStatusIcon.setImageDrawable(Util.getStatusIconDrawable(getActivity(), allow)); mStatusIcon.setVisibility(View.VISIBLE); mPackageNameText.setText(data.getString(DETAILS_COLUMN_PACKAGE)); mAppUidText.setText(data.getString(DETAILS_COLUMN_UID)); mRequestDetailText.setText( Util.getUidName(getActivity(), data.getInt(DETAILS_COLUMN_EXEC_UID), true)); mCommandText.setText(data.getString(DETAILS_COLUMN_EXEC_CMD)); mStatusText.setText(allow==1? R.string.allowed:R.string.denied); if (mToggleButton != null) { mToggleButton.setText(allow==1?R.string.deny:R.string.allow); } mAllow = allow; String notificationsStr = data.getString(DETAILS_COLUMN_NOTIFICATIONS); String loggingStr = data.getString(DETAILS_COLUMN_LOGGING); if (notificationsStr == null && loggingStr == null) { mUseAppSettings = true; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); mNotificationsEnabled = prefs.getBoolean(Preferences.NOTIFICATIONS, true); mLoggingEnabled = prefs.getBoolean(Preferences.LOGGING, true); } else { mUseAppSettings = false; mNotificationsEnabled = notificationsStr.equals("1")?true:false; mLoggingEnabled = loggingStr.equals("1")?true:false; } } mReady = true; break; case LOG_LOADER: mAdapter.swapCursor(data); break; } } @Override public void onLoaderReset(Loader<Cursor> loader) { if (loader.getId() == LOG_LOADER) { mAdapter.swapCursor(null); } } private class MoreOptionsPopup extends BetterPopupWindow implements OnCheckedChangeListener { private CheckBox mUseAppCheckBox; private CheckBox mNotificationsCheckBox; private CheckBox mLoggingCheckBox; public MoreOptionsPopup(View anchor) { super(anchor); } @Override protected void onCreate() { LayoutInflater inflater = (LayoutInflater) this.anchor.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup root = (ViewGroup) inflater.inflate(R.layout.more_options, null); mUseAppCheckBox = (CheckBox) root.findViewById(R.id.check_use_app); mUseAppCheckBox.setChecked(mUseAppSettings); mNotificationsCheckBox = (CheckBox)root.findViewById(R.id.check_notifications); mNotificationsCheckBox.setChecked(mNotificationsEnabled); mNotificationsCheckBox.setEnabled(!mUseAppSettings); mLoggingCheckBox = (CheckBox)root.findViewById(R.id.check_logging); mLoggingCheckBox.setChecked(mLoggingEnabled); mLoggingCheckBox.setEnabled(!mUseAppSettings); mUseAppCheckBox.setOnCheckedChangeListener(this); mNotificationsCheckBox.setOnCheckedChangeListener(this); mLoggingCheckBox.setOnCheckedChangeListener(this); this.setContentView(root); } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { ContentResolver cr = getActivity().getContentResolver(); if (buttonView.getId() == R.id.check_use_app) { mUseAppSettings = isChecked; mNotificationsCheckBox.setEnabled(!mUseAppSettings); mLoggingCheckBox.setEnabled(!mUseAppSettings); ContentValues values = new ContentValues(); values.put(Apps.NOTIFICATIONS, isChecked?null:mNotificationsEnabled); values.put(Apps.LOGGING, isChecked?null:mLoggingEnabled); cr.update(ContentUris.withAppendedId(Apps.CONTENT_URI, mShownIndex), values, null, null); if (isChecked) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); mNotificationsEnabled = prefs.getBoolean(Preferences.NOTIFICATIONS, true); mNotificationsCheckBox.setChecked(mNotificationsEnabled); mLoggingEnabled = prefs.getBoolean(Preferences.LOGGING, true); mLoggingCheckBox.setChecked(mLoggingEnabled); } } else { ContentValues values = new ContentValues(); values.put(Apps.NOTIFICATIONS, mNotificationsCheckBox.isChecked()); values.put(Apps.LOGGING, mLoggingCheckBox.isChecked()); cr.update(ContentUris.withAppendedId(Apps.CONTENT_URI, mShownIndex), values, null, null); } } } }