diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ae6826b0b92..c3f8e29278d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1319,6 +1319,16 @@ + + + + + + + diff --git a/res/layout/installed_app_details.xml b/res/layout/installed_app_details.xml index 5c6867cf791..099df64484c 100644 --- a/res/layout/installed_app_details.xml +++ b/res/layout/installed_app_details.xml @@ -342,7 +342,7 @@ - diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9649ed3acb5..ba69f13bda9 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -27,6 +27,8 @@ 16dip 26dip 40dip + 16dip + 8dip 252dip 440dip diff --git a/res/values/strings.xml b/res/values/strings.xml index 7ce4c25f8a0..58e2cfb99b8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -20,6 +20,9 @@ "No" + + Create + Unknown @@ -2328,6 +2331,8 @@ Storage Launch by default + + Defaults Screen compatibility @@ -2366,6 +2371,8 @@ Uninstall updates You\'ve chosen to launch this app by default for some actions. + + You\'ve chosen to allow this app to create widgets and access their data. No defaults set. @@ -2788,6 +2795,15 @@ the final name for Gadgets/Widgets, so please translate both for now. --> Choose widget + + Create widget and allow access? + + + After you create the widget, %1$s can access all data it displays. + + + Always allow %1$s to create widgets and access their data + %1$dd %2$dh %3$dm %4$ds diff --git a/src/com/android/settings/AllowBindAppWidgetActivity.java b/src/com/android/settings/AllowBindAppWidgetActivity.java new file mode 100644 index 00000000000..2f54f8eb83e --- /dev/null +++ b/src/com/android/settings/AllowBindAppWidgetActivity.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012 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.settings; + +import android.app.AlertDialog; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.LayoutInflater; +import android.widget.CheckBox; + +import com.android.internal.app.AlertActivity; +import com.android.internal.app.AlertController; + +/** + * This activity is displayed when an app launches the BIND_APPWIDGET intent. This allows apps + * that don't have the BIND_APPWIDGET permission to bind specific widgets. + */ +public class AllowBindAppWidgetActivity extends AlertActivity implements + DialogInterface.OnClickListener { + + private CheckBox mAlwaysUse; + private int mAppWidgetId; + private ComponentName mComponentName; + private String mCallingPackage; + private AppWidgetManager mAppWidgetManager; + + // Indicates whether this activity was closed because of a click + private boolean mClicked; + + public void onClick(DialogInterface dialog, int which) { + if (which == AlertDialog.BUTTON_POSITIVE) { + // By default, set the result to cancelled + setResult(RESULT_CANCELED); + if (mAppWidgetId != -1 && mComponentName != null && mCallingPackage != null) { + try { + mAppWidgetManager.bindAppWidgetId(mAppWidgetId, mComponentName); + Intent result = new Intent(); + result.putExtra("EXTRA_APPWIDGET_ID", mAppWidgetId); + setResult(RESULT_OK); + } catch (Exception e) { + Log.v("BIND_APPWIDGET", "Error binding widget with id " + + mAppWidgetId + " and component " + mComponentName); + } + } + boolean alwaysAllowBind = mAlwaysUse.isChecked(); + if (alwaysAllowBind != mAppWidgetManager.hasBindAppWidgetPermission(mCallingPackage)) { + mAppWidgetManager.setBindAppWidgetPermission(mCallingPackage, alwaysAllowBind); + } + } + finish(); + } + + protected void onDestroy() { + if (!mClicked) { + setResult(RESULT_CANCELED); + finish(); + } + super.onDestroy(); + } + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + CharSequence label = ""; + if (intent != null) { + try { + mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + mComponentName = (ComponentName) + intent.getParcelableExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER); + mCallingPackage = getCallingPackage(); + PackageManager pm = getPackageManager(); + ApplicationInfo ai = pm.getApplicationInfo(mCallingPackage, 0); + label = pm.getApplicationLabel(ai); + } catch (Exception e) { + mAppWidgetId = -1; + mComponentName = null; + mCallingPackage = null; + Log.v("BIND_APPWIDGET", "Error getting parameters"); + setResult(RESULT_CANCELED); + finish(); + return; + } + } + AlertController.AlertParams ap = mAlertParams; + ap.mTitle = getString(R.string.allow_bind_app_widget_activity_allow_bind_title); + ap.mMessage = getString(R.string.allow_bind_app_widget_activity_allow_bind, label); + ap.mPositiveButtonText = getString(R.string.create); + ap.mNegativeButtonText = getString(android.R.string.cancel); + ap.mPositiveButtonListener = this; + ap.mNegativeButtonListener = this; + LayoutInflater inflater = + (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); + mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse); + mAlwaysUse.setText(getString(R.string.allow_bind_app_widget_activity_always_allow_bind, label)); + + mAlwaysUse.setPadding(mAlwaysUse.getPaddingLeft(), + mAlwaysUse.getPaddingTop(), + mAlwaysUse.getPaddingRight(), + (int) (mAlwaysUse.getPaddingBottom() + + getResources().getDimension(R.dimen.bind_app_widget_dialog_checkbox_bottom_padding))); + + mAppWidgetManager = AppWidgetManager.getInstance(this); + mAlwaysUse.setChecked(mAppWidgetManager.hasBindAppWidgetPermission(mCallingPackage)); + + setupAlert(); + } +} diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index fcdf899abbf..6fa20cea904 100644 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -29,7 +29,9 @@ import android.app.Fragment; import android.app.INotificationManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; +import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -52,13 +54,15 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.preference.PreferenceActivity; +import android.text.SpannableString; +import android.text.TextUtils; import android.text.format.Formatter; +import android.text.style.BulletSpan; import android.util.Log; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -import android.content.ComponentName; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -91,6 +95,7 @@ public class InstalledAppDetails extends Fragment private PackageManager mPm; private IUsbManager mUsbManager; + private AppWidgetManager mAppWidgetManager; private DevicePolicyManager mDpm; private ApplicationsState mState; private ApplicationsState.AppEntry mAppEntry; @@ -346,6 +351,7 @@ public class InstalledAppDetails extends Fragment mPm = getActivity().getPackageManager(); IBinder b = ServiceManager.getService(Context.USB_SERVICE); mUsbManager = IUsbManager.Stub.asInterface(b); + mAppWidgetManager = AppWidgetManager.getInstance(getActivity()); mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); mCanBeOnSdCardChecker = new CanBeOnSdCardChecker(); @@ -493,20 +499,55 @@ public class InstalledAppDetails extends Fragment // Intent list cannot be null. so pass empty list List intentList = new ArrayList(); mPm.getPreferredActivities(intentList, prefActList, packageName); - if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list"); + if (localLOGV) + Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); boolean hasUsbDefaults = false; try { hasUsbDefaults = mUsbManager.hasDefaults(packageName); } catch (RemoteException e) { Log.e(TAG, "mUsbManager.hasDefaults", e); } - TextView autoLaunchView = (TextView)mRootView.findViewById(R.id.auto_launch); - if (prefActList.size() <= 0 && !hasUsbDefaults) { - // Disable clear activities button - autoLaunchView.setText(R.string.auto_launch_disable_text); - mActivitiesButton.setEnabled(false); + boolean hasBindAppWidgetPermission = + mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); + + TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults; + if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { + resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); } else { - autoLaunchView.setText(R.string.auto_launch_enable_text); + boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; + + if (hasBindAppWidgetPermission) { + autoLaunchTitleView.setText(R.string.auto_launch_label_generic); + } else { + autoLaunchTitleView.setText(R.string.auto_launch_label); + } + + CharSequence text = null; + int bulletIndent = getResources() + .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); + if (autoLaunchEnabled) { + CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); + SpannableString s = new SpannableString(autoLaunchEnableText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + if (hasBindAppWidgetPermission) { + CharSequence alwaysAllowBindAppWidgetsText = + getText(R.string.always_allow_bind_appwidgets_text); + SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), + 0, alwaysAllowBindAppWidgetsText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + autoLaunchView.setText(text); mActivitiesButton.setEnabled(true); mActivitiesButton.setOnClickListener(this); } @@ -549,6 +590,13 @@ public class InstalledAppDetails extends Fragment return true; } + private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) { + title.setText(R.string.auto_launch_label); + autoLaunchView.setText(R.string.auto_launch_disable_text); + // Disable clear activities button + mActivitiesButton.setEnabled(false); + } + private void setIntentAndFinish(boolean finish, boolean appChanged) { if(localLOGV) Log.i(TAG, "appChanged="+appChanged); Intent intent = new Intent(); @@ -905,7 +953,11 @@ public class InstalledAppDetails extends Fragment } catch (RemoteException e) { Log.e(TAG, "mUsbManager.clearDefaults", e); } - mActivitiesButton.setEnabled(false); + mAppWidgetManager.setBindAppWidgetPermission(packageName, false); + TextView autoLaunchTitleView = + (TextView) mRootView.findViewById(R.id.auto_launch_title); + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); } else if(v == mClearDataButton) { if (mAppEntry.info.manageSpaceActivityName != null) { if (!Utils.isMonkeyRunning()) {