/* * Copyright 2015. Appsi Mobile * * 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.appsimobile.appsii.module.home; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.support.annotation.DrawableRes; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; import android.widget.Toast; import com.appsimobile.appsii.DrawableCompat; import com.appsimobile.appsii.R; import com.appsimobile.appsii.dagger.AppInjector; import com.appsimobile.appsii.module.home.config.HomeItemConfiguration; import com.appsimobile.appsii.module.home.config.HomeItemConfigurationHelper; import java.io.BufferedInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URISyntaxException; import javax.inject.Inject; /** * Created by nick on 29/01/15. */ public class IntentViewHolder extends BaseViewHolder implements View.OnClickListener, HomeItemConfigurationHelper.ConfigurationListener, PopupMenu.OnMenuItemClickListener { static final String INTENT_TYPE_ACTIVITY = "app"; static final String INTENT_TYPE_SHORTCUT = "shortcut"; static final String INTENT_TYPE_SHELL = "shell"; static final String INTENT_TYPE_BROADCAST = "broadcast"; static final String INTENT_TYPE_SERVICE = "service"; final TextView mTextView; final ImageView mAppImage; private final int mPrimaryColor; private final int mWidgetColor; @Inject HomeItemConfiguration mConfigurationHelper; @Nullable String mAction; @Nullable String mCategory; @Nullable String mIntent; @Nullable String mIcon; @Nullable String mTitle; @Nullable String mPackageName; @Nullable String mClassName; @Nullable String mIntentType; @Inject PackageManager mPackageManager; public IntentViewHolder(HomeViewWrapper view) { super(view); AppInjector.inject(this); mAppImage = (ImageView) view.findViewById(R.id.app_image); mTextView = (TextView) view.findViewById(R.id.primary_text); mOverflow.setOnClickListener(this); Context context = view.getContext(); final TypedArray a = context.obtainStyledAttributes( new int[]{R.attr.colorPrimary, R.attr.colorAccent, R.attr.colorPrimaryDark, R.attr.appsiHomeWidgetPrimaryColor, }); mPrimaryColor = a.getColor(0, Color.BLACK); mWidgetColor = a.getColor(3, Color.BLACK); a.recycle(); } @Override void updateConfiguration() { long cellId = mHomeItem.mId; mPackageName = mConfigurationHelper.getProperty(cellId, "package", null); mClassName = mConfigurationHelper.getProperty(cellId, "class", null); mAction = mConfigurationHelper.getProperty(cellId, "action", null); mCategory = mConfigurationHelper.getProperty(cellId, "category", null); mTitle = mConfigurationHelper.getProperty(cellId, "title", null); mIntent = mConfigurationHelper.getProperty(cellId, "intent", null); mIcon = mConfigurationHelper.getProperty(cellId, "icon", null); mIntentType = mConfigurationHelper.getProperty(cellId, "intentType", INTENT_TYPE_ACTIVITY); if (mTitle == null) { int titleRes = getTitleFromString(mIcon); if (titleRes == 0) { String title = getTitleFromPackageManager(); if (title == null) { mTextView.setText(R.string.untitled); } else { mTextView.setText(title); } } else { mTextView.setText(titleRes); } } else { mTextView.setText(mTitle); } int drawableRes = getIconFromString(mIcon); if (drawableRes == 0) { Context context = getContext(); Drawable icon; if (INTENT_TYPE_SHORTCUT.equals(mIntentType)) { icon = decodeIconFromFile(cellId); } else { icon = decodeIconFromPackageManager(); } if (icon == null) { icon = context.getResources().getDrawable(android.R.drawable.sym_def_app_icon); } mAppImage.setImageDrawable(icon); } else { Context context = getContext(); Drawable drawable = context.getResources().getDrawable(drawableRes); DrawableCompat.setTintColorCompat(drawable, mPrimaryColor); mAppImage.setImageDrawable(drawable); } } @Override void bind(HomeItem item, int heightPx) { super.bind(item, heightPx); updateConfiguration(); mChildView.setOnClickListener(this); } private String getTitleFromPackageManager() { ComponentInfo info = getComponentInfoByPackageNameAndClassName(mPackageManager); if (info == null) return null; return String.valueOf(info.loadLabel(mPackageManager)); } private Drawable decodeIconFromPackageManager() { if (INTENT_TYPE_SHELL.equals(mIntentType)) return null; PackageManager pm = mPackageManager; ComponentInfo info; if (mPackageName != null && mClassName != null) { info = getComponentInfoByPackageNameAndClassName(pm); } else { info = getComponentInfo(pm); } if (info != null) { return info.loadIcon(pm); } return null; } private Drawable decodeIconFromFile(long cellId) { File iconFile = IntentEditorFragment.createIconFile(getContext(), cellId); try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(iconFile)); try { Bitmap bitmap = BitmapFactory.decodeStream(in); return new BitmapDrawable(itemView.getResources(), bitmap); } finally { in.close(); } } catch (IOException e) { Log.wtf("HomeAdapter", "error decoding icon", e); } return null; } @Nullable ComponentInfo getComponentInfo(PackageManager pm) { if (mIntent == null) return null; Intent intent; try { intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME); } catch (URISyntaxException e) { return null; } ComponentName componentName = intent.getComponent(); return getComponentInfo(pm, componentName); } @Nullable ComponentInfo getComponentInfoByPackageNameAndClassName(PackageManager pm) { if (mPackageName == null || mClassName == null) return null; String className = mClassName; if (className.startsWith(".")) { className = mPackageName + mClassName; } ComponentName componentName = new ComponentName(mPackageName, className); return getComponentInfo(pm, componentName); } private ComponentInfo getComponentInfo(PackageManager pm, ComponentName componentName) { if (componentName == null) return null; try { switch (mIntentType) { case INTENT_TYPE_BROADCAST: return pm.getReceiverInfo(componentName, 0); case INTENT_TYPE_SERVICE: return pm.getServiceInfo(componentName, 0); default: return pm.getActivityInfo(componentName, 0); } } catch (PackageManager.NameNotFoundException e) { Log.wtf("IntentViewHolder", "name not found", e); return null; } } @DrawableRes int getIconFromString(String key) { if (key == null) return 0; switch (key) { case "play_music": return R.drawable.ic_music_black_48dp; case "camera": return R.drawable.ic_photo_camera_black_48dp; default: return 0; } } @StringRes int getTitleFromString(String key) { if (key == null) return 0; switch (key) { case "play_music": return R.string.music; case "camera": return R.string.camera; default: return 0; } } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.overflow) { showOverflowMenu(v); } else { if (INTENT_TYPE_SHELL.equals(mIntentType)) { String command = mConfigurationHelper.getProperty(mHomeItem.mId, "shell", null); boolean asRoot = "true".equals( mConfigurationHelper.getProperty(mHomeItem.mId, "as_root", "false")); performShellCommand(command, asRoot); return; } Intent intent = createIntent(); if (intent != null) { try { switch (mIntentType) { case INTENT_TYPE_BROADCAST: getContext().sendBroadcast(intent); break; case INTENT_TYPE_SERVICE: getContext().startService(intent); break; default: getContext().startActivity(intent); break; } } catch (ActivityNotFoundException | SecurityException e) { Toast.makeText(getContext(), R.string.intent_error_message, Toast.LENGTH_SHORT).show(); Log.e("App Launcher", "Error launching intent: " + intent, e); } } } } private void showOverflowMenu(View v) { PopupMenu popupMenu = new PopupMenu(v.getContext(), v); MenuInflater inflater = popupMenu.getMenuInflater(); Menu menu = popupMenu.getMenu(); inflater.inflate(R.menu.home_item_app, menu); popupMenu.setOnMenuItemClickListener(this); popupMenu.show(); } private void performShellCommand(final String command, final boolean asRoot) { if (command != null) { AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { try { if (asRoot) { Process su = Runtime.getRuntime().exec("su"); DataOutputStream outputStream = new DataOutputStream(su.getOutputStream()); outputStream.writeBytes(command + "\n"); outputStream.flush(); outputStream.writeBytes("exit\n"); outputStream.flush(); su.waitFor(); } else { Runtime.getRuntime().exec(command); } } catch (InterruptedException | IOException e) { Log.wtf("IntentViewHolder", "error executing command", e); } return null; } }; task.execute(); } } Intent createIntent() { switch (mIntentType) { case INTENT_TYPE_ACTIVITY: case INTENT_TYPE_BROADCAST: case INTENT_TYPE_SERVICE: Intent intent = new Intent(); if (!TextUtils.isEmpty(mAction)) { intent.setAction(mAction); } if (!TextUtils.isEmpty(mCategory)) { intent.addCategory(mCategory); } if (!TextUtils.isEmpty(mPackageName)) { intent.setPackage(mCategory); } if (!TextUtils.isEmpty(mClassName) && !TextUtils.isEmpty(mPackageName)) { intent.setClassName(mPackageName, mClassName); } return intent; case INTENT_TYPE_SHORTCUT: if (mIntent == null) return null; try { return Intent.parseUri(mIntent, 0); } catch (URISyntaxException e) { Log.wtf("HomeAdapter", "can't parse intent uri: " + mIntent); } case INTENT_TYPE_SHELL: default: return null; } } private Context getContext() { return itemView.getContext(); } @Override public boolean onMenuItemClick(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.action_cell_app_prefs) { Context context = getContext(); Intent intent = new Intent(context, CellIntentEditorActivity.class); intent.putExtra(CellIntentEditorActivity.EXTRA_CELL_ID, mHomeItem.mId); context.startActivity(intent); return true; } return false; } }