<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+ <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
<application
android:name="ExplorerApplication"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ** Copyright (C) 2012 The CyanogenMod 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_shortAnimTime">
+
+ <item
+ android:drawable="@android:color/holo_blue_dark"
+ android:state_pressed="true"/>
+ <item
+ android:drawable="@android:color/holo_blue_light"
+ android:state_enabled="true"
+ android:state_focused="true"/>
+ <item
+ android:drawable="@android:color/holo_blue_light"
+ android:state_enabled="true"
+ android:state_checked="true"/>
+ <item
+ android:drawable="@android:color/holo_blue_light"
+ android:state_enabled="true"
+ android:state_selected="true"/>
+ <item
+ android:drawable="@android:color/transparent"/>
+
+</selector>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ** Copyright (C) 2012 The CyanogenMod 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_margin="@dimen/default_margin">
+
+ <GridView android:id="@+id/associations_gridview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:stretchMode="columnWidth"
+ android:scrollbars="vertical"
+ android:horizontalSpacing="@dimen/default_margin"
+ android:verticalSpacing="@dimen/default_margin"
+ android:numColumns="3" />
+
+ <CheckBox android:id="@+id/associations_remember"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/extra_margin"
+ android:textAppearance="@style/secondary_text_appearance"
+ android:text="@string/associations_dialog_remember" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ** Copyright (C) 2012 The CyanogenMod 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.\r
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/grid_width"
+ android:layout_height="wrap_content"
+ android:background="@drawable/holo_selection"
+ android:orientation="vertical" >
+
+ <ImageView android:id="@+id/associations_item_icon"
+ android:width="@dimen/grid_image_width"
+ android:height="@dimen/grid_image_height"
+ android:layout_width="@dimen/grid_width"
+ android:layout_height="@dimen/grid_height"
+ android:contentDescription="@null"
+ android:layout_centerInParent="true"
+ android:scaleType="center"
+ android:src="@null" />
+
+ <TextView android:id="@+id/associations_item_text"
+ android:layout_width="@dimen/grid_width"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:textAppearance="@style/secondary_text_appearance"
+ android:paddingBottom="@dimen/default_margin"
+ android:gravity="center"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+</RelativeLayout>
\ No newline at end of file
<!-- The navigation grid item height -->
<dimen name="navigation_grid_item_height">48dp</dimen>
+ <!-- The grid width -->
+ <dimen name="grid_width">96dp</dimen>
+ <!-- The grid height -->
+ <dimen name="grid_height">96dp</dimen>
+ <!-- The grid image width -->
+ <dimen name="grid_image_width">48dp</dimen>
+ <!-- The grid image height -->
+ <dimen name="grid_image_height">48dp</dimen>
+
<!-- The popup dimension width -->
<dimen name="popup_width">200dp</dimen>
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- The system directory -->
+ <string name="system_dir">/system</string>
+
<!-- The size of the buffers use by the console (in bytes). Default: 0.5 Mb -->
<integer name="buffer_size">512</integer>
<!-- An operation can't not be cancelled -->
<string name="msgs_read_only_filesystem">The filesystem is read-only. Try to mount the
filesystem as read-write before do a write operation.</string>
+ <!-- An association of an action failed -->
+ <string name="msgs_action_association_failed">The association of the action failed.</string>
<!-- A message advise before close the application -->
<string name="msgs_push_again_to_exit">Push again back button to exit.</string>
<string name="actions_ask_remove_file">Are you sure you want to delete this file
permanently?</string>
-
<!-- Enter Name Dialog * Label -->
<string name="input_name_dialog_label">Name:</string>
<!-- Enter Name Dialog * Message * Empty name -->
<!-- Enter Name Dialog * Message * Name exists -->
<string name="input_name_dialog_message_name_exists">The namealready exists.</string>
+ <!-- Associations Dialog * Title -->
+ <string name="associations_dialog_title">Associations</string>
+ <!-- Associations Dialog * Remember the user action -->
+ <string name="associations_dialog_remember">Remember selection</string>
+ <!-- Associations Dialog * Open with Title -->
+ <string name="associations_dialog_openwith_title">Open with</string>
+ <!-- Associations Dialog * Open action -->
+ <string name="associations_dialog_openwith_action">Open</string>
<!-- Inline Autocomplete Widget * Tab message nothing to complete -->
<string name="inline_autocomplete_tab_nothing_to_complete_msg">Nothing to complete</string>
The required exitcode element must have the required attributes commandId and commandPath
with the expected command for retrieve the exit code of the executed command
-->
-<CommandList xmlns:androcommandId="http://schemas.android.com/apk/res/android">
+<CommandList xmlns="http://schemas.android.com/apk/res/com.cyanogenmod.explorer">
<!-- Start code (append to commands; for retrieve the exit code) -->
<startcode commandId="startcode" commandPath="/system/xbin/echo %1$s0%2$s ; " />
<!-- Exit code (append to commands; for retrieve the exit code) -->
name: (string) The name of the bookmark
directory: (string) The folder where to point to
-->
-<Bookmarks xmlns:androcommandId="http://schemas.android.com/apk/res/android">
+<Bookmarks xmlns="http://schemas.android.com/apk/res/com.cyanogenmod.explorer">
<bookmark name="@string/bookmarks_root_folder" directory="/" />
- <bookmark name="@string/bookmarks_system_folder" directory="/system" />
+ <bookmark name="@string/bookmarks_system_folder" directory="@string/system_dir" />
</Bookmarks>
\ No newline at end of file
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.util.Log;
import com.cyanogenmod.explorer.console.ConsoleHolder;
import com.cyanogenmod.explorer.preferences.ExplorerSettings;
import com.cyanogenmod.explorer.preferences.Preferences;
+import com.cyanogenmod.explorer.util.ExceptionUtil;
import com.cyanogenmod.explorer.util.FileHelper;
import com.cyanogenmod.explorer.util.MimeTypeHelper;
* @return Application The application singleton reference
* @hide
*/
- public static Application getInstance() {
+ public static ExplorerApplication getInstance() {
return sApp;
}
return sBackgroundConsole.getConsole();
}
+
+
+ /**
+ * Method that check if the app is signed with the platform signature
+ *
+ * @param ctx The current context
+ * @return boolean If the app is signed with the platform signature
+ */
+ public static boolean isAppPlatformSignature(Context ctx) {
+ // TODO This need to be improved, checking if the app is really with the platform signature
+ try {
+ // For now only check that the app is installed in system directory
+ PackageManager pm = ctx.getPackageManager();
+ String appDir = pm.getApplicationInfo(ctx.getPackageName(), 0).sourceDir;
+ String systemDir = ctx.getString(R.string.system_dir);
+ return appDir.startsWith(systemDir);
+
+ } catch (Exception e) {
+ ExceptionUtil.translateException(ctx, e, true, false);
+ }
+ return false;
+ }
+
}
}
if (TAG_BOOKMARK.equals(element)) {
- CharSequence name =
- getString(parser.getAttributeResourceValue(
- R.styleable.Bookmark_name, 0));
- CharSequence directory =
- parser.getAttributeValue(R.styleable.Bookmark_directory);
+ CharSequence name = null;
+ CharSequence directory = null;
+
+ try {
+ name =
+ getString(parser.getAttributeResourceValue(
+ R.styleable.Bookmark_name, 0));
+ } catch (Exception e) {/**NON BLOCK**/}
+ try {
+ directory =
+ getString(parser.getAttributeResourceValue(
+ R.styleable.Bookmark_directory, 0));
+ } catch (Exception e) {/**NON BLOCK**/}
+ if (directory == null) {
+ directory =
+ parser.getAttributeValue(R.styleable.Bookmark_directory);
+ }
if (name != null && directory != null) {
bookmarks.add(
new Bookmark(
--- /dev/null
+/*
+ * Copyright (C) 2012 The CyanogenMod 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.cyanogenmod.explorer.adapters;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.cyanogenmod.explorer.R;
+
+import java.util.List;
+
+/**
+ * An implementation of {@link ArrayAdapter} for display associations.
+ */
+public class AssociationsAdapter
+ extends ArrayAdapter<ResolveInfo> implements View.OnClickListener {
+
+ /**
+ * A class that conforms with the ViewHolder pattern to performance
+ * the list view rendering.
+ */
+ private static class ViewHolder {
+ /**
+ * @hide
+ */
+ public ViewHolder() {
+ super();
+ }
+ int mPosition;
+ ImageView mIvIcon;
+ TextView mTvName;
+ }
+
+ /**
+ * A class that holds the full data information.
+ */
+ private static class DataHolder {
+ /**
+ * @hide
+ */
+ public DataHolder() {
+ super();
+ }
+ Drawable mDwIcon;
+ String mName;
+ }
+
+
+
+ private DataHolder[] mData;
+ private final OnItemClickListener mOnItemClickListener;
+
+ //The resource item layout
+ private static final int RESOURCE_LAYOUT = R.layout.associations_item;
+
+ //The resource of the item icon
+ private static final int RESOURCE_ITEM_ICON = R.id.associations_item_icon;
+ //The resource of the item name
+ private static final int RESOURCE_ITEM_NAME = R.id.associations_item_text;
+
+ /**
+ * Constructor of <code>AssociationsAdapter</code>.
+ *
+ * @param context The current context
+ * @param intents The intents info
+ * @param onItemClickListener The listener for listen action clicks
+ */
+ public AssociationsAdapter(
+ Context context, List<ResolveInfo> intents, OnItemClickListener onItemClickListener) {
+ super(context, RESOURCE_ITEM_NAME, intents);
+ this.mOnItemClickListener = onItemClickListener;
+
+ //Do cache of the data for better performance
+ processData();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ processData();
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Method that dispose the elements of the adapter.
+ */
+ public void dispose() {
+ clear();
+ this.mData = null;
+ }
+
+ /**
+ * Method that process the data before use {@link #getView} method.
+ */
+ private void processData() {
+ this.mData = new DataHolder[getCount()];
+ int cc = getCount();
+ for (int i = 0; i < cc; i++) {
+ //Intent info
+ ResolveInfo intentInfo = getItem(i);
+
+ //Build the data holder
+ this.mData[i] = new AssociationsAdapter.DataHolder();
+ this.mData[i].mDwIcon = intentInfo.loadIcon(getContext().getPackageManager());
+ this.mData[i].mName =
+ intentInfo.loadLabel(getContext().getPackageManager()).toString();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ //Check to reuse view
+ View v = convertView;
+ if (v == null) {
+ //Create the view holder
+ LayoutInflater li =
+ (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ v = li.inflate(RESOURCE_LAYOUT, parent, false);
+ ViewHolder viewHolder = new AssociationsAdapter.ViewHolder();
+ viewHolder.mPosition = position;
+ viewHolder.mIvIcon = (ImageView)v.findViewById(RESOURCE_ITEM_ICON);
+ viewHolder.mTvName = (TextView)v.findViewById(RESOURCE_ITEM_NAME);
+ v.setTag(viewHolder);
+ }
+
+ //Retrieve data holder
+ final DataHolder dataHolder = this.mData[position];
+
+ //Retrieve the view holder
+ ViewHolder viewHolder = (ViewHolder)v.getTag();
+
+ //Set the data
+ viewHolder.mPosition = position;
+ viewHolder.mIvIcon.setImageDrawable(dataHolder.mDwIcon);
+ viewHolder.mTvName.setText(dataHolder.mName);
+ v.setOnClickListener(this);
+
+ //Return the view
+ return v;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onClick(View v) {
+ ViewHolder viewHolder = (ViewHolder)v.getTag();
+ this.mOnItemClickListener.onItemClick(null, v, viewHolder.mPosition, v.getId());
+ }
+
+}
*/
public Query fillSlots(List<String> queries) {
int cc = queries.size();
- for (int i = 0; i < queries.size(); i++) {
+ for (int i = 0; i < cc; i++) {
if (i > this.mQUERIES.length) {
break;
}
public List<String> getQueries() {
List<String> queries = new ArrayList<String>(getSlotsCount());
int cc = this.mQUERIES.length;
- for (int i = 0; i < this.mQUERIES.length; i++) {
+ for (int i = 0; i < cc; i++) {
if (this.mQUERIES[i] != null && this.mQUERIES[i].length() > 0) {
queries.add(this.mQUERIES[i]);
}
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2012 The CyanogenMod 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.cyanogenmod.explorer.ui.dialogs;
+
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.PatternMatcher;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.CheckBox;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+import android.widget.Toast;
+
+import com.cyanogenmod.explorer.ExplorerApplication;
+import com.cyanogenmod.explorer.R;
+import com.cyanogenmod.explorer.adapters.AssociationsAdapter;
+import com.cyanogenmod.explorer.util.DialogHelper;
+import com.cyanogenmod.explorer.util.ExceptionUtil;
+
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A class that wraps a dialog for showing the list of available intents that can handle an
+ * action. This dialog allows predetermined
+ */
+public class AssociationsDialog implements OnItemClickListener {
+
+ private static final String TAG = "AssociationsDialog"; //$NON-NLS-1$
+
+ final Context mContext;
+ private final List<ResolveInfo> mIntents;
+ private final ResolveInfo mPreferred;
+ /**
+ * @hide
+ */
+ final Intent mRequestIntent;
+
+ private AlertDialog mDialog;
+ /**
+ * @hide
+ */
+ GridView mGrid;
+ /**
+ * @hide
+ */
+ CheckBox mRemember;
+
+ private boolean mLoaded;
+
+ /**
+ * Constructor of <code>AssociationsDialog</code>.
+ *
+ * @param context The current context
+ * @param icon The icon of the dialog
+ * @param title The title dialog
+ * @param action The title of the action button
+ * @param requestIntent The original request
+ * @param intents The list of available intents that can handle an action
+ * @param preferred The preferred intent. null if no preferred exists
+ */
+ public AssociationsDialog(
+ Context context, int icon, String title, String action,
+ Intent requestIntent, List<ResolveInfo> intents, ResolveInfo preferred) {
+ super();
+
+ //Save the data
+ this.mContext = context;
+ this.mRequestIntent = requestIntent;
+ this.mIntents = intents;
+ this.mPreferred = preferred;
+ this.mLoaded = false;
+
+ //Initialize dialog
+ init(icon, title, action);
+ }
+
+ /**
+ * Method that initializes the dialog.
+ *
+ * @param context The current context
+ * @param icon The icon of the dialog
+ * @param title The title of the dialog
+ * @param action The title of the action button
+ */
+ private void init(int icon, String title, String action) {
+ boolean isPlatformSigned =
+ ExplorerApplication.isAppPlatformSignature(this.mContext);
+
+ //Create the layout, and retrieve the views
+ LayoutInflater li =
+ (LayoutInflater)this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = li.inflate(R.layout.associations_dialog, null, false);
+ this.mRemember = (CheckBox)v.findViewById(R.id.associations_remember);
+ this.mRemember.setVisibility(isPlatformSigned ? View.VISIBLE : View.GONE);
+ this.mGrid = (GridView)v.findViewById(R.id.associations_gridview);
+ this.mGrid.setAdapter(new AssociationsAdapter(this.mContext, this.mIntents, this));
+
+ // Ensure a default title dialog
+ String dialogTitle = title;
+ if (dialogTitle == null) {
+ dialogTitle = this.mContext.getString(R.string.associations_dialog_title);
+ }
+
+ //Create the dialog
+ this.mDialog = DialogHelper.createDialog(
+ this.mContext,
+ icon,
+ dialogTitle,
+ v);
+ this.mDialog.setButton(
+ DialogInterface.BUTTON_POSITIVE,
+ action,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ResolveInfo ri = getSelected();
+ Intent intent = new Intent(AssociationsDialog.this.mRequestIntent);
+ intent.setFlags(
+ intent.getFlags() &~
+ Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT |
+ Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
+ intent.setComponent(
+ new ComponentName(
+ ri.activityInfo.applicationInfo.packageName,
+ ri.activityInfo.name));
+
+ // Open the intent (and remember the action is the check is marked)
+ onIntentSelected(
+ ri,
+ intent,
+ AssociationsDialog.this.mRemember.isChecked());
+ }
+ });
+ this.mDialog.setButton(
+ DialogInterface.BUTTON_NEGATIVE,
+ this.mContext.getString(android.R.string.cancel),
+ (DialogInterface.OnClickListener)null);
+ }
+
+ /**
+ * Method that shows the dialog.
+ */
+ public void show() {
+ this.mDialog.show();
+
+ // Set user preferences
+ this.mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(false);
+ this.mGrid.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!checkUserPreferences()) {
+ // Recall for check user preferences
+ AssociationsDialog.this.mGrid.postDelayed(this, 50L);
+ }
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ deselectAll();
+ ((ViewGroup)view).setSelected(true);
+
+ // Enable action button
+ this.mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(true);
+ }
+
+ /**
+ * Method that check the user preferences
+ *
+ * @return boolean Indicates if the user preferences was set
+ * @hide
+ */
+ boolean checkUserPreferences() {
+ boolean ret = false;
+ if (!this.mLoaded) {
+ // Check that the view is loaded
+ if ((ViewGroup)this.mGrid.getChildAt(0) == null) return false;
+
+ if (this.mPreferred != null) {
+ boolean found = false;
+ int cc = this.mIntents.size();
+ for (int i = 0; i < cc; i++) {
+ ResolveInfo info = this.mIntents.get(i);
+ if (info.activityInfo.name.equals(this.mPreferred.activityInfo.name)) {
+ // Select the item
+ ViewGroup item = (ViewGroup)this.mGrid.getChildAt(i);
+ if (item != null) {
+ if (!item.isSelected()) {
+ onItemClick(null, item, i, item.getId());
+
+ // Not allow to revert remember status
+ this.mRemember.setChecked(true);
+ this.mRemember.setEnabled(false);
+ ret = false;
+ } else {
+ this.mLoaded = true;
+ ret = true;
+ }
+ }
+ found = true;
+ break;
+ }
+ }
+
+ // Is there is no user preferences?
+ if (!found) {
+ this.mLoaded = true;
+ ret = true;
+ }
+ } else {
+ // There is no user preferences
+ this.mLoaded = true;
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Method that returns if the preferred intent is still selected
+ *
+ * @return if the preferred intent is selected
+ */
+ private boolean isPreferredSelected() {
+ if (this.mPreferred != null) {
+ int cc = this.mIntents.size();
+ for (int i = 0; i < cc; i++) {
+ ResolveInfo info = this.mIntents.get(i);
+ if (info.activityInfo.name.equals(this.mPreferred.activityInfo.name)) {
+ ViewGroup item = (ViewGroup)this.mGrid.getChildAt(i);
+ if (item != null) {
+ if (item.isSelected()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Method that deselect all the items of the grid view
+ */
+ private void deselectAll() {
+ ListAdapter adapter = this.mGrid.getAdapter();
+ int cc = adapter.getCount();
+ for (int i = 0; i < cc; i++) {
+ ViewGroup item = (ViewGroup)this.mGrid.getChildAt(i);
+ item.setSelected(false);
+ }
+ }
+
+ /**
+ * Method that deselect all the items of the grid view
+ *
+ * @return ResolveInfo The selected item
+ * @hide
+ */
+ ResolveInfo getSelected() {
+ AssociationsAdapter adapter = (AssociationsAdapter)this.mGrid.getAdapter();
+ int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ ViewGroup item = (ViewGroup)this.mGrid.getChildAt(i);
+ if (item.isSelected()) {
+ return adapter.getItem(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Method that opens the associated intent.
+ *
+ * @param ri The resolve information
+ * @param intent The user selection
+ * @param remember If the user selection, must be remembered
+ * @see ResolverActivity (copied from)
+ * @hide
+ */
+ @SuppressWarnings({"boxing"})
+ void onIntentSelected(ResolveInfo ri, Intent intent, boolean remember) {
+ if (remember) {
+ // Build a reasonable intent filter, based on what matched.
+ IntentFilter filter = new IntentFilter();
+
+ if (intent.getAction() != null) {
+ filter.addAction(intent.getAction());
+ }
+ Set<String> categories = intent.getCategories();
+ if (categories != null) {
+ for (String cat : categories) {
+ filter.addCategory(cat);
+ }
+ }
+ filter.addCategory(Intent.CATEGORY_DEFAULT);
+
+ int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
+ Uri data = intent.getData();
+ if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
+ String mimeType = intent.resolveType(this.mContext);
+ if (mimeType != null) {
+ try {
+ filter.addDataType(mimeType);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ Log.w(TAG, e);
+ filter = null;
+ }
+ }
+ }
+ if (data != null && data.getScheme() != null && filter != null) {
+ // We need the data specification if there was no type,
+ // OR if the scheme is not one of our magical "file:"
+ // or "content:" schemes (see IntentFilter for the reason).
+ if (cat != IntentFilter.MATCH_CATEGORY_TYPE
+ || (!"file".equals(data.getScheme()) //$NON-NLS-1$
+ && !"content".equals(data.getScheme()))) { //$NON-NLS-1$
+ filter.addDataScheme(data.getScheme());
+
+ // Look through the resolved filter to determine which part
+ // of it matched the original Intent.
+ Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
+ if (aIt != null) {
+ while (aIt.hasNext()) {
+ IntentFilter.AuthorityEntry a = aIt.next();
+ if (a.match(data) >= 0) {
+ int port = a.getPort();
+ filter.addDataAuthority(a.getHost(),
+ port >= 0 ? Integer.toString(port) : null);
+ break;
+ }
+ }
+ }
+ Iterator<PatternMatcher> pIt = ri.filter.pathsIterator();
+ if (pIt != null) {
+ String path = data.getPath();
+ while (path != null && pIt.hasNext()) {
+ PatternMatcher p = pIt.next();
+ if (p.match(path)) {
+ filter.addDataPath(p.getPath(), p.getType());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Register preferred association is only allowed by platform signature
+ // CMExplorer will be signed with this signature, but when is launch from
+ // inside ADT, the app is signed with testkey.
+ // Ignore it if the preferred can be saved. Only notify the user and open the
+ // intent
+ boolean isPlatformSigned =
+ ExplorerApplication.isAppPlatformSignature(this.mContext);
+ if (isPlatformSigned) {
+ if (filter != null && !isPreferredSelected()) {
+ try {
+ AssociationsAdapter adapter = (AssociationsAdapter)this.mGrid.getAdapter();
+ final int cc = adapter.getCount();
+ ComponentName[] set = new ComponentName[cc];
+ int bestMatch = 0;
+ for (int i = 0; i < cc; i++) {
+ ResolveInfo r = adapter.getItem(i);
+ set[i] = new ComponentName(
+ r.activityInfo.packageName, r.activityInfo.name);
+ if (r.match > bestMatch) bestMatch = r.match;
+ }
+
+ // Use reflection to access the hidden replacePreferredActivity method
+ // FIXME This need to be tested on a CM compilation. Now
+ // replacePreferredActivity is not working. Check also
+ // the use of addPreferredActivity.
+ PackageManager pm = this.mContext.getPackageManager();
+ Method m = pm.getClass().getMethod(
+ "replacePreferredActivity", //$NON-NLS-1$
+ IntentFilter.class, int.class,
+ ComponentName[].class, ComponentName.class);
+ m.invoke(pm, filter, bestMatch, set, intent.getComponent());
+
+ } catch (Exception e) {
+ // Capture the exception
+ ExceptionUtil.translateException(this.mContext, e, true, false);
+ Toast.makeText(
+ this.mContext,
+ R.string.msgs_action_association_failed,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
+
+ if (intent != null) {
+ this.mContext.startActivity(intent);
+ }
+ }
+}
import com.cyanogenmod.explorer.model.Bookmark.BOOKMARK_TYPE;
import com.cyanogenmod.explorer.model.FileSystemObject;
import com.cyanogenmod.explorer.preferences.Bookmarks;
+import com.cyanogenmod.explorer.ui.dialogs.AssociationsDialog;
import com.cyanogenmod.explorer.ui.dialogs.FsoPropertiesDialog;
import com.cyanogenmod.explorer.util.CommandHelper;
import com.cyanogenmod.explorer.util.DialogHelper;
*
* @param ctx The current context
* @param fso The file system object
- * @param choose If allow the user to select the app to open with
+ * @param choose If allow the user to select the application to open with
*/
public static void openFileSystemObject(
final Context ctx, final FileSystemObject fso, final boolean choose) {
packageManager.
queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ // Retrieve the preferred activity that can handle the file
+ final ResolveInfo mPreferredInfo = packageManager.resolveActivity(intent, 0);
+
// Now we have the list of activities that can handle the file. The next steps are:
//
// 1.- If choose, then show open with dialog
- // 1.- If info size == 0. No default application, then show open with dialog
- // 2.- If !choose, seek inside our database the default activity for the extension
+ // 2.- If info size == 0. No default application, then show open with dialog
+ // 3.- If !choose, seek inside our database the default activity for the extension
// and open the file with this application
- // 3.- If no default activity saved, then use system default
+ // 4.- If no default activity saved, then use system default
- if (choose || info.size() == 0) {
- // Show open with dialog
- return;
+ // No registered application
+ if (info.size() == 0) {
+ DialogHelper.createWarningDialog(ctx, R.string.msgs_not_registered_app);
}
+ // Is a simple open and we have an application that can handle the file?
+ if (!choose && (mPreferredInfo.match != 0 || info.size() == 1)) {
+ ctx.startActivity(intent);
+ return;
+ }
-
- // 2.- info size == 0
-
-
-
-// if (info.size() == 0) {
-// DialogHelper.createWarningDialog(ctx, R.string.msgs_not_registered_app);
-// }
-//
-// // Open with?
-// if (choose) {
-// String title = ctx.getString(R.string.actions_menu_open_with);
-// ((Activity)ctx).startActivity(Intent.createChooser(intent,title));
-//
-// } else {
-// ((Activity)ctx).startActivity(intent);
-// }
+ // Otherwise, we have to show the open with dialog
+ AssociationsDialog dialog =
+ new AssociationsDialog(
+ ctx,
+ R.drawable.ic_holo_light_open,
+ ctx.getString(R.string.associations_dialog_openwith_title),
+ ctx.getString(R.string.associations_dialog_openwith_action),
+ intent,
+ info,
+ mPreferredInfo);
+ dialog.show();
} catch (Exception e) {
ExceptionUtil.translateException(ctx, e);