diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 4a27b0c1b..e99ad7f92 100755 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -42,6 +42,7 @@ + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 43cc1c9bc..5830ef285 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -154,7 +154,6 @@ dependencies { implementation project (':preferences') implementation project (':resources') implementation project (':treeview') - implementation project (':xml-inflater') implementation fileTree (dir: 'libs', include: ['*.jar']) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 346903511..2cd1a62ff 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -44,10 +44,6 @@ - . - * - */ -package com.itsaky.androidide; - -import android.annotation.SuppressLint; -import android.app.ProgressDialog; -import android.content.ClipData; -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CheckedTextView; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.RadioButton; -import android.widget.RelativeLayout; -import android.widget.SeekBar; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.core.content.ContextCompat; -import androidx.core.view.GravityCompat; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.transition.Fade; -import androidx.transition.Slide; -import androidx.transition.TransitionManager; -import androidx.transition.TransitionSet; - -import com.blankj.utilcode.util.DeviceUtils; -import com.itsaky.androidide.adapters.WidgetGroupItemAdapter; -import com.itsaky.androidide.adapters.WidgetItemAdapter; -import com.itsaky.androidide.app.IDEActivity; -import com.itsaky.androidide.databinding.ActivityDesignerBinding; -import com.itsaky.androidide.fragments.sheets.AttrEditorSheet; -import com.itsaky.androidide.fragments.sheets.ProgressSheet; -import com.itsaky.androidide.models.UIWidget; -import com.itsaky.androidide.models.UIWidgetGroup; -import com.itsaky.androidide.tasks.TaskExecutor; -import com.itsaky.androidide.ui.WidgetDragData; -import com.itsaky.androidide.ui.WidgetDragListener; -import com.itsaky.androidide.ui.WidgetDragShadowBuilder; -import com.itsaky.androidide.ui.WidgetTouchListener; -import com.itsaky.androidide.utils.DialogUtils; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IInflateListener; -import com.itsaky.inflater.ILayoutInflater; -import com.itsaky.inflater.IView; -import com.itsaky.inflater.IViewGroup; -import com.itsaky.inflater.LayoutInflaterConfiguration; -import com.itsaky.inflater.impl.BaseView; - -import org.jetbrains.annotations.Contract; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; - -public class DesignerActivity extends IDEActivity - implements WidgetItemAdapter.OnDragStartListener, - AttrEditorSheet.OnViewDeletionFailedListener, - ILayoutInflater.ContextProvider { - - public static final String KEY_LAYOUT_PATH = "designer_layoutPath"; - public static final String KEY_RES_DIRS = "designer_resDirs"; - public static final String KEY_GENERATED_CODE = "designer_xmlCode"; - public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET"; - public static final String DRAGGING_WIDGET_MIME = "application/ide_widget"; - private static final ILogger LOG = ILogger.newInstance("DesignerActivity"); - public final String EDITOR_SHEET_DIALOG_TAG = "attribute_editor_dialog"; - private final boolean isTablet = DeviceUtils.isTablet(); - private final List widgetGroups = new ArrayList<>(); - private ActivityDesignerBinding mBinding; - private UIWidgetGroup checkedWidgetCategory; - private ILayoutInflater layoutInflater; - private IViewGroup inflatedRoot; - private File layout; - private boolean inflationFailed = false; - private AttrEditorSheet mEditorSheet; - // This will make sure to apply listeners, background and data to view that are inflated from - // XML. - private final IInflateListener mInflateListener = - new IInflateListener() { - - @Override - public void onBeginInflate() {} - - @Override - public void onInflateView(IView view, IViewGroup parent) { - if (view instanceof IViewGroup) { - View v = view.asView(); - v.setBackgroundResource(R.drawable.bg_design_container_background); - v.setOnDragListener(getOnDragListener((IViewGroup) view)); - } - - setupInflatedView(view); - setDragDataToInflatedView(view); - } - - @Override - public void onApplyAttribute(IAttribute attr, IView view) {} - - @Override - public void onFinishInflate(IView rootView) { - if (rootView instanceof IViewGroup) { - inflatedRoot = (IViewGroup) rootView; - } - } - }; - - @Override - public void onBackPressed() { - - if (inflationFailed) { - super.onBackPressed(); - return; - } - - // When the user presses the back button, set the activity result and finish this activity - - final var progress = new ProgressSheet(); - progress.setSubMessageEnabled(true); - progress.setShowTitle(false); - progress.setMessage(getString(R.string.title_generating_xml)); - progress.setSubMessage(getString(R.string.please_wait)); - progress.show(getSupportFragmentManager(), "generate_code_progress"); - - final var future = - CompletableFuture.supplyAsync( - () -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode())); - future.whenComplete( - (s, throwable) -> { - progress.dismiss(); - - if (throwable != null) { - notifyXmlGenerationFailed(throwable); - return; - } - - try { - finishWithResult(future.get()); - } catch (ExecutionException | InterruptedException e) { - notifyXmlGenerationFailed(e); - } - }); - } - - private void notifyXmlGenerationFailed(Throwable error) { - final var errorMessage = getString(R.string.msg_generate_xml_failed); - LOG.error(errorMessage, error); - - DialogUtils.newYesNoDialog( - this, - getString(R.string.title_code_generation_failed), - getString(R.string.msg_code_generation_failed), - (dialog1, which) -> finishWithError(), - (dialog1, which) -> dialog1.dismiss()) - .show(); - } - - private void finishWithError() { - final var intent = new Intent(); - setResult(-123, intent); - finish(); - } - - private void finishWithResult(String result) { - final var intent = new Intent(); - intent.putExtra(KEY_GENERATED_CODE, result); - setResult(RESULT_OK, intent); - finish(); - } - - @Override - public void onDragStarted(View view) { - mBinding.getRoot().closeDrawer(GravityCompat.START); - } - - @Override - public boolean onDeletionFailed(@NonNull IView view) { - final var v = view.asView(); - if (mBinding.layoutContainer.indexOfChild(v) >= 0) { - mBinding.layoutContainer.removeView(v); - return true; - } - return false; - } - - @Override - public Context getContext() { - return this; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setSupportActionBar(mBinding.toolbar); - Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); - - final var toggle = - new ActionBarDrawerToggle( - this, mBinding.getRoot(), mBinding.toolbar, R.string.app_name, R.string.app_name) { - - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - super.onDrawerSlide(drawerView, slideOffset); - if (isTablet) { - mBinding.mainContainer.setTranslationX(drawerView.getWidth() * slideOffset); - } - } - }; - mBinding.getRoot().addDrawerListener(toggle); - toggle.syncState(); - - final var extras = getIntent().getExtras(); - final var path = extras.getString(KEY_LAYOUT_PATH, null); - final var name = path.substring(path.lastIndexOf(File.separator) + 1); - final var resDirs = extras.getStringArrayList(KEY_RES_DIRS); - getSupportActionBar().setTitle(name); - - try { - this.layout = new File(path); - LOG.debug("Inflating layout file:", this.layout); - LOG.debug("Resource directories:", TextUtils.join(",\n", resDirs)); - - //noinspection deprecation - final var pd = ProgressDialog.show(this, null, getString(R.string.please_wait), true, false); - final var dirs = resDirs.stream().map(File::new).collect(Collectors.toSet()); - final var future = getApp().createInflaterConfig(this, dirs); - - TaskExecutor.executeAsyncProvideError( - future::get, - (config, throwable) -> { - pd.dismiss(); - - if (config == null || throwable != null) { - LOG.error("Unable to create inflater configuration", throwable); - return; - } - - inflatePath(path, config); - }); - } catch (Throwable th) { - onLayoutInflationFailed(th); - } - - setupWidgets(); - - // Always open the drawer if this device is a tablet. - if (isTablet) { - mBinding.getRoot().setScrimColor(Color.TRANSPARENT); - mBinding.getRoot().openDrawer(GravityCompat.START); - mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); - } - } - - @Override - protected View bindLayout() { - this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater()); - return mBinding.getRoot(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - LOG.logThis(); - if (isAttrEditorShowing()) { - getAttrEditorSheet().dismiss(); - } - - this.mEditorSheet = null; - - // Release the reference to inflate listener and context from the layout inflater - // Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment - // manager - try { - if (layoutInflater == null) { - LOG.debug("Skipping layout inflater context reference release. Inflater is null"); - return; - } - - this.layoutInflater.unregisterListener(mInflateListener); - this.layoutInflater.resetContextProvider(null); - } catch (Throwable e) { - LOG.error("Unable to release activity references from ILayoutInflater", e); - } - mBinding = null; - } - - private boolean isAttrEditorShowing() { - return getAttrEditorSheet().getDialog() != null && getAttrEditorSheet().getDialog().isShowing(); - } - - private AttrEditorSheet getAttrEditorSheet() { - return this.mEditorSheet == null - ? mEditorSheet = new AttrEditorSheet().setLayout(this.layout) - : mEditorSheet; - } - - protected void onLayoutInflationFailed(Throwable th) { - mBinding.layoutContainer.removeAllViews(); - mBinding.layoutContainer.addView(createErrorText(th)); - LOG.error(getString(R.string.err_cannot_inflate_layout), th); - inflationFailed = true; - } - - private void inflatePath(String path, LayoutInflaterConfiguration config) { - try { - this.layoutInflater = ILayoutInflater.newInstance(config); - this.layoutInflater.resetContextProvider(this); - this.layoutInflater.registerInflateListener(this.mInflateListener); - final IView view = this.layoutInflater.inflatePath(path, mBinding.layoutContainer); - - if (this.inflatedRoot != null) { - this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot)); - } - - mBinding.layoutContainer.addView(view.asView()); - mBinding.layoutContainer.setOnClickListener( - v -> mBinding.getRoot().openDrawer(GravityCompat.START)); - - tryDismiss(); - } catch (Throwable error) { - onLayoutInflationFailed(error); - } - } - - private void tryDismiss() { - final var manager = getSupportFragmentManager(); - final var frag = manager.findFragmentByTag(EDITOR_SHEET_DIALOG_TAG); - if (frag != null) { - final var transaction = manager.beginTransaction(); - transaction.remove(frag); - transaction.commit(); - } - } - - private void setupInflatedView(@NonNull IView view) { - final var listener = - new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick); - view.asView().setOnTouchListener(listener); - - setDragDataToInflatedView(view); - } - - private boolean onLayoutViewLongClick(@NonNull IView view) { - final var shadow = new WidgetDragShadowBuilder(view.asView()); - final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG); - final var clip = - new ClipData( - DesignerActivity.DRAGGING_WIDGET_TAG, - new String[] {DesignerActivity.DRAGGING_WIDGET_MIME}, - item); - final var data = view.getExtraData(); - - view.asView().startDragAndDrop(clip, shadow, data, 0); - view.removeFromParent(); - - return true; - } - - private void onLayoutViewClick(@NonNull IView view) { - if (isAttrEditorShowing()) { - getAttrEditorSheet().dismiss(); - } - - getAttrEditorSheet().setSelectedView(view); - - if (!getSupportFragmentManager().isDestroyed()) { - getAttrEditorSheet().show(getSupportFragmentManager(), EDITOR_SHEET_DIALOG_TAG); - } - } - - private void setupWidgets() { - final var android = new UIWidgetGroup(getString(R.string.widget_group_android)); - android.addChild( - new UIWidget(getString(R.string.widget_button), R.drawable.ic_widget_button, Button.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_checkbox), R.drawable.ic_widget_checkbox, CheckBox.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_checked_textview), - R.drawable.ic_widget_checked_textview, - CheckedTextView.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_edittext), R.drawable.ic_widget_edittext, EditText.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_image_button), - R.drawable.ic_widget_image, - ImageButton.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_image_view), R.drawable.ic_widget_image, ImageView.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_progressbar), - R.drawable.ic_widget_progress, - ProgressBar.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_radio_button), - R.drawable.ic_widget_radio_button, - RadioButton.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_seekbar), R.drawable.ic_widget_seekbar, SeekBar.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_spinner), R.drawable.ic_widget_spinner, Spinner.class)); - android.addChild( - new UIWidget( - getString(R.string.widget_textview), R.drawable.ic_widget_textview, TextView.class)); - - // IMPORTANT: Do not select any other groups here - android.setSelected(true); - this.checkedWidgetCategory = android; - widgetGroups.add(android); - - final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts)); - layouts.addChild( - new UIWidget( - getString(R.string.layout_linear), R.drawable.ic_widget_linear, LinearLayout.class)); - layouts.addChild( - new UIWidget( - getString(R.string.layout_relative), - R.drawable.ic_widget_relative, - RelativeLayout.class)); - - widgetGroups.add(layouts); - - final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup); - mBinding.groupItems.setAdapter(adapter); - - showWidgetGroup(android); - } - - @SuppressLint("NotifyDataSetChanged") - private void showWidgetGroup(@NonNull UIWidgetGroup group) { - final var children = group.getChildren(); - this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this)); - this.checkedWidgetCategory.setSelected(false); - this.checkedWidgetCategory = group; - this.checkedWidgetCategory.setSelected(true); - - TransitionManager.beginDelayedTransition( - mBinding.navigation, - new TransitionSet().addTransition(new Slide(Gravity.END)).addTransition(new Fade())); - - this.mBinding.groupItems.getAdapter().notifyDataSetChanged(); - } - - @NonNull - private TextView createErrorText(Throwable th) { - final TextView error = new TextView(this); - error.setText(createErrorMessage(th)); - error.setLayoutParams(new ViewGroup.LayoutParams(-1, -1)); - error.setTextColor(ContextCompat.getColor(this, android.R.color.black)); - error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white)); - error.setGravity(Gravity.CENTER); - return error; - } - - private CharSequence createErrorMessage(final Throwable th) { - final var sb = new StringBuilder(); - - if (th == null) { - sb.append("Unknown error"); - return sb; - } - - var err = th; - var first = true; - while (err != null) { - if (!first) { - sb.append("\n"); - } - sb.append(err.getMessage()); - err = err.getCause(); - } - - return sb; - } - - @NonNull - @Contract("_ -> new") - private View.OnDragListener getOnDragListener(IViewGroup group) { - // WidgetDragListener cannot be reused - // This is because they keep a reference to the view group in which dragged view will be - // added - - return new WidgetDragListener(this, group, this::setupInflatedView); - } - - private void setDragDataToInflatedView(@NonNull IView view) { - view.setExtraData(new WidgetDragData(true, view, null)); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index f55997beb..a162e4d52 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -54,9 +54,6 @@ import android.widget.CheckBox; import android.widget.LinearLayout; import androidx.activity.OnBackPressedCallback; -import androidx.activity.result.ActivityResult; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.GravityInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -128,19 +125,14 @@ import com.itsaky.androidide.views.editor.IDEEditor; import com.itsaky.androidide.xml.resources.ResourceTableRegistry; import com.itsaky.androidide.xml.versions.ApiVersionsRegistry; import com.itsaky.androidide.xml.widgets.WidgetTableRegistry; -import com.itsaky.inflater.values.ValuesTableFactory; import com.itsaky.toaster.Toaster; -import org.jetbrains.annotations.Contract; - import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -167,7 +159,7 @@ public class EditorActivity extends IDEActivity private final EditorEventListener mBuildEventListener = new EditorEventListener(); private final EditorActivityLifecyclerObserver mLifecycleObserver = new EditorActivityLifecyclerObserver(); - + private ActivityEditorBinding mBinding; private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding; private final LogReceiver mLogReceiver = new LogReceiver().setLogListener(this::appendApkLog); @@ -176,7 +168,6 @@ public class EditorActivity extends IDEActivity private TextSheetFragment mDaemonStatusFragment; private ProgressSheet mSearchingProgress; private AlertDialog mFindInProjectDialog; - private ActivityResultLauncher mUIDesignerLauncher; private BottomSheetBehavior mEditorBottomSheet; private EditorViewModel mViewModel; private final ServiceConnection mGradleServiceConnection = @@ -404,7 +395,7 @@ public class EditorActivity extends IDEActivity if (mBinding == null) { return null; } - + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt(i); if (file.equals(editor.getFile())) { @@ -429,7 +420,7 @@ public class EditorActivity extends IDEActivity IntentUtils.openImage(this, file); return null; } - + if (mBinding == null) { return null; } @@ -713,27 +704,7 @@ public class EditorActivity extends IDEActivity } } - public void previewLayout() { - try { - - if (getCurrentEditor() == null || getCurrentEditor().getFile() == null) { - LOG.error("No file is opened. Cannot preview layout."); - return; - } - - saveAll(false); - - final Intent intent = new Intent(this, DesignerActivity.class); - intent.putExtra( - DesignerActivity.KEY_LAYOUT_PATH, getCurrentEditor().getFile().getAbsolutePath()); - intent.putStringArrayListExtra(DesignerActivity.KEY_RES_DIRS, getResourceDirPaths()); - LOG.info("Launching UI Designer..."); - mUIDesignerLauncher.launch(intent); - } catch (Throwable th) { - LOG.error(getString(string.err_cannot_preview_layout), th); - toast(string.msg_cannot_preview_layout, Toaster.Type.ERROR); - } - } + public void previewLayout() {} public boolean saveAll(boolean notify) { return saveAll(notify, false); @@ -931,10 +902,6 @@ public class EditorActivity extends IDEActivity setupContainers(); setupDiagnosticInfo(); - mUIDesignerLauncher = - registerForActivityResult( - new ActivityResultContracts.StartActivityForResult(), this::onGetUIDesignerResult); - EditorActivityActions.register(this); } @@ -1081,25 +1048,6 @@ public class EditorActivity extends IDEActivity } catch (Throwable th) { // ignored } - - CompletableFuture.runAsync( - () -> { - final var resDirs = getResourceDirs(); - resDirs.removeIf(Objects::isNull); - ValuesTableFactory.setupWithResDirectories(resDirs.toArray(new File[0])); - }); - } - - private Set getResourceDirs() { - return ProjectManager.INSTANCE.getApplicationResDirectories(); - } - - @NonNull - private ArrayList getResourceDirPaths() { - return getResourceDirs().stream() - .filter(Objects::nonNull) - .map(File::getAbsolutePath) - .collect(Collectors.toCollection(ArrayList::new)); } private void setupDrawerToggle() { @@ -1297,31 +1245,6 @@ public class EditorActivity extends IDEActivity builder.show(); } - @Contract(pure = true) - private void onGetUIDesignerResult(@NonNull ActivityResult result) { - if (mBinding == null) { - return; - } - final var index = mBinding.editorContainer.getDisplayedChild(); - final var editor = getEditorAtIndex(index); - if (editor != null && result.getResultCode() == RESULT_OK) { - final var data = result.getData(); - if (data != null && data.hasExtra(DesignerActivity.KEY_GENERATED_CODE)) { - final var code = data.getStringExtra(DesignerActivity.KEY_GENERATED_CODE); - editor.getEditor().replaceContent(code); - editor.markModified(); - saveAll(); - } else { - final var msg = getString(string.msg_invalid_designer_result); - toast(msg, Toaster.Type.ERROR); - LOG.error(msg, "Data returned by UI Designer is null or is invalid."); - } - } else { - LOG.error( - "UI Designer returned an invalid result code.", "Result code: " + result.getResultCode()); - } - } - private void openTerminal() { final Intent intent = new Intent(this, TerminalActivity.class); intent.putExtra( diff --git a/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java deleted file mode 100644 index 00856fc54..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.adapters; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.resources.R; -import com.itsaky.attrinfo.models.Attr; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -/** - * An {@link com.itsaky.androidide.adapters.IconTextAdapter} for {@link - * com.itsaky.attrinfo.models.Attr}. - * - * @author Akash Yadav - */ -public class AttrListAdapter extends IconTextAdapter { - - private final Consumer clickConsumer; - private final List attrs; - - public AttrListAdapter(Consumer clickConsumer, List attrs) { - this.clickConsumer = clickConsumer; - if (attrs == null) { - attrs = new ArrayList<>(); - } - - this.attrs = attrs; - } - - @Override - public int getItemCount() { - return attrs.size(); - } - - @Override - public void onBindViewHolder(@NonNull VH holder, int position) { - super.onBindViewHolder(holder, position); - - if (clickConsumer != null) { - holder.binding.getRoot().setOnClickListener(v -> clickConsumer.accept(getItemAt(position))); - } - } - - @NonNull - @Override - public Attr getItemAt(int index) { - return attrs.get(index); - } - - @Override - public int getIconResource(int index) { - return R.drawable.ic_xml_attribute; - } - - @NonNull - @Override - public String getItemText(int index) { - return getItemAt(index).name; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueCompletionAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/AttrValueCompletionAdapter.java deleted file mode 100644 index 450bbf9e3..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueCompletionAdapter.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.adapters; - -import android.content.Context; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.Filter; -import android.widget.Filterable; -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -/** - * Adapter for showing the autocomplete items while editing attribute values in UI Designer. - * - * @author Akash Yadav - */ -public class AttrValueCompletionAdapter extends BaseAdapter implements Filterable { - - private final List items; - private final List filtered; - private final Context context; - private final Filter mFilter = - new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - final var result = new FilterResults(); - if (TextUtils.isEmpty(constraint)) { - final var list = new ArrayList<>(items); - result.values = list; - result.count = list.size(); - return result; - } - - final var match = constraint.toString().trim().toLowerCase(Locale.ROOT); - final var list = new ArrayList(); - for (String item : items) { - if (item.contains(match)) { - list.add(item); - } - } - - result.values = list; - result.count = list.size(); - return result; - } - - @Override - protected void publishResults(CharSequence constraint, @NonNull FilterResults results) { - //noinspection unchecked - List items = (List) results.values; - Objects.requireNonNull(items); - - filtered.clear(); - filtered.addAll(items); - - if (items.isEmpty()) { - notifyDataSetInvalidated(); - } else { - notifyDataSetChanged(); - } - } - }; - - public AttrValueCompletionAdapter(Context context, List items) { - this.context = context; - this.items = items; - this.filtered = new ArrayList<>(items); - } - - @Override - public int getCount() { - return this.filtered.size(); - } - - @Override - public Object getItem(int position) { - return filtered.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = - ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)) - .inflate(android.R.layout.simple_list_item_1, parent, false); - } - - ((TextView) convertView).setText(getItem(position).toString()); - - return convertView; - } - - @Override - public Filter getFilter() { - return mFilter; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java deleted file mode 100644 index 73774932d..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/AttrValueFormatTabAdapter.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.adapters; - -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; - -import com.itsaky.androidide.fragments.attr.BaseValueEditorFragment; -import com.itsaky.androidide.models.XMLAttribute; -import com.itsaky.androidide.utils.ILogger; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Adapter for value format tabs of attribute editor in UI Designer. - * - * @author Akash Yadav - */ -@SuppressWarnings("deprecation") -public class AttrValueFormatTabAdapter extends FragmentStatePagerAdapter { - - private static final ILogger LOG = ILogger.newInstance("AttrValueEditorTabAdapter"); - private final List mFragments = new ArrayList<>(); - private final List mTitles = new ArrayList<>(); - private final XMLAttribute attribute; - - public AttrValueFormatTabAdapter(@NonNull FragmentManager manager, XMLAttribute attribute) { - super(manager); - Objects.requireNonNull(attribute); - - this.attribute = attribute; - } - - public void addFragment(final Class fragment, String title) { - Objects.requireNonNull(fragment); - Objects.requireNonNull(title); - if (TextUtils.isEmpty(title.trim())) { - throw new IllegalArgumentException("Invalid tab title"); - } - - mTitles.add(capitalize(title)); - mFragments.add(createFragment(fragment, title)); - } - - @NonNull - private String capitalize(String title) { - title = title.trim(); - - final var sb = new StringBuilder(); - for (int i = 0; i < title.length(); i++) { - var ch = title.charAt(i); - if (i == 0) { - ch = Character.toUpperCase(ch); - } else { - ch = Character.toLowerCase(ch); - } - - sb.append(ch); - } - - return sb.toString(); - } - - @NonNull - public Fragment createFragment( - Class clazz, @NonNull final String name) { - try { - final var frag = clazz.newInstance(); - final var args = new Bundle(); - args.putParcelable(BaseValueEditorFragment.KEY_ATTR, attribute); - args.putString(BaseValueEditorFragment.KEY_NAME, name); - frag.setArguments(args); - - return frag; - } catch (Throwable th) { - LOG.error("Unable to create value editor fragment", th); - throw new RuntimeException(th); - } - } - - public void removeAll() { - mFragments.clear(); - mTitles.clear(); - } - - @NonNull - @Override - public Fragment getItem(int position) { - return mFragments.get(position); - } - - @Override - public int getCount() { - return mFragments.size(); - } - - @Nullable - @Override - public CharSequence getPageTitle(int position) { - return mTitles.get(position); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java deleted file mode 100755 index 392f5969e..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.adapters; - -import android.graphics.Color; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; - -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.models.IconTextListItem; -import com.itsaky.androidide.models.UIWidgetGroup; - -import java.util.List; - -public class WidgetGroupItemAdapter extends SimpleIconTextAdapter - implements SimpleIconTextAdapter.OnBindListener { - - private final OnGroupClickListener clickListener; - - public WidgetGroupItemAdapter( - @NonNull List groups, @Nullable OnGroupClickListener clickListener) { - super(groups); - this.clickListener = clickListener; - - super.setOnBindListener(this); - } - - @Override - public boolean onBind(IconTextListItem item, VH holder, int position) { - // Ignored - return false; - } - - @Override - public void postBind(IconTextListItem item, @NonNull VH holder, int position) { - final var binding = holder.binding; - final var group = (UIWidgetGroup) item; - - if (group.isSelected()) { - binding - .getRoot() - .setBackground( - ContextCompat.getDrawable( - binding.getRoot().getContext(), R.drawable.bg_widget_group_selected)); - } else { - binding.getRoot().setBackgroundColor(Color.TRANSPARENT); - } - - if (this.clickListener != null) { - binding.getRoot().setOnClickListener(v -> this.clickListener.onGroupClick(group)); - } - } - - public interface OnGroupClickListener { - void onGroupClick(UIWidgetGroup group); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java deleted file mode 100755 index d5199ebf1..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.adapters; - -import android.content.ClipData; -import android.view.View; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.DesignerActivity; -import com.itsaky.androidide.models.IconTextListItem; -import com.itsaky.androidide.models.UIWidget; -import com.itsaky.androidide.ui.WidgetDragData; -import com.itsaky.androidide.ui.WidgetDragShadowBuilder; - -import java.util.List; - -public class WidgetItemAdapter extends SimpleIconTextAdapter - implements SimpleIconTextAdapter.OnBindListener { - - private final OnDragStartListener dragStartListener; - - public WidgetItemAdapter(List widgets, OnDragStartListener dragStartListener) { - super(widgets); - this.dragStartListener = dragStartListener; - - setOnBindListener(this); - } - - @Override - public boolean onBind(IconTextListItem item, @NonNull VH holder, int position) { - return false; // Returning true will not set the text and icon - } - - @Override - public void postBind(IconTextListItem item, VH holder, int position) { - final var binding = holder.binding; - final var root = binding.getRoot(); - final var widget = (UIWidget) getItemAt(position); - - root.setOnLongClickListener( - v -> { - final var dragData = new WidgetDragData(false, null, widget); - final var shadow = new WidgetDragShadowBuilder(binding.icon); - final var dataItem = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG); - final var data = - new ClipData( - DesignerActivity.DRAGGING_WIDGET_TAG, - new String[] {DesignerActivity.DRAGGING_WIDGET_MIME}, - dataItem); - binding.icon.startDragAndDrop(data, shadow, dragData, 0); - - if (this.dragStartListener != null) { - this.dragStartListener.onDragStarted(v); - } - - return true; - }); - } - - public interface OnDragStartListener { - void onDragStarted(View view); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java deleted file mode 100644 index f1be6324f..000000000 --- a/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.adapters; - -import android.graphics.Typeface; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; -import android.view.LayoutInflater; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.recyclerview.widget.RecyclerView; - -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding; -import com.itsaky.androidide.models.XMLAttribute; - -import java.util.List; - -public class XMLAttributeListAdapter extends RecyclerView.Adapter { - - private final List attributes; - private final OnClickListener clickListener; - - public XMLAttributeListAdapter(List attributes, OnClickListener clickListener) { - this.attributes = attributes; - this.clickListener = clickListener; - } - - @NonNull - @Override - public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new VH( - LayoutAttrEditorSheetItemBinding.inflate( - LayoutInflater.from(parent.getContext()), parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull VH holder, int position) { - final var binding = holder.binding; - final var attr = attributes.get(position); - final var sb = new SpannableStringBuilder(); - sb.append(attr.getAttributeName()); - if (attr.isApplied()) { - sb.append(" = "); - sb.append(attr.getValue(), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - final var length = attr.getValue().length(); - sb.setSpan( - new ForegroundColorSpan( - ContextCompat.getColor(binding.text.getContext(), R.color.primaryTextColor)), - sb.length() - length, - sb.length() - 1, - SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - binding.text.setText(sb); - binding - .getRoot() - .setOnClickListener( - v -> { - if (this.clickListener != null) { - this.clickListener.onClick(binding, attr); - } - }); - } - - @Override - public int getItemCount() { - return attributes.size(); - } - - static class VH extends RecyclerView.ViewHolder { - LayoutAttrEditorSheetItemBinding binding; - - VH(@NonNull LayoutAttrEditorSheetItemBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - } - - public interface OnClickListener { - void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.java b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.java index eb9560d8c..af430e220 100755 --- a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.java +++ b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.java @@ -23,36 +23,20 @@ import android.net.Uri; import com.blankj.utilcode.util.ThrowableUtils; import com.itsaky.androidide.BuildConfig; import com.itsaky.androidide.CrashHandlerActivity; -import com.itsaky.androidide.resources.R; import com.itsaky.androidide.events.AppEventsIndex; import com.itsaky.androidide.events.LspApiEventsIndex; import com.itsaky.androidide.events.LspJavaEventsIndex; import com.itsaky.androidide.events.ProjectsApiEventsIndex; -import com.itsaky.androidide.preferences.internal.XmlPreferencesKt; -import com.itsaky.androidide.projects.ProjectResourceTable; import com.itsaky.androidide.utils.ILogger; -import com.itsaky.attrinfo.AttrInfo; -import com.itsaky.inflater.ILayoutInflater; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.LayoutInflaterConfiguration; -import com.itsaky.sdk.SDKInfo; import com.itsaky.toaster.ToastUtilsKt; import com.itsaky.toaster.Toaster; -import com.itsaky.widgets.WidgetInfo; import org.greenrobot.eventbus.EventBus; -import java.io.File; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - public class IDEApplication extends BaseApplication { private static final ILogger LOG = ILogger.newInstance("IDEApplication"); private static IDEApplication instance; - private static SDKInfo sdkInfo; - private IResourceTable mResTable; private Thread.UncaughtExceptionHandler uncaughtExceptionHandler; public static IDEApplication getInstance() { @@ -72,8 +56,6 @@ public class IDEApplication extends BaseApplication { .addIndex(new LspApiEventsIndex()) .addIndex(new LspJavaEventsIndex()) .installDefaultEventBus(); - - getApiInformation(); } private void handleCrash(Thread thread, Throwable th) { @@ -95,54 +77,6 @@ public class IDEApplication extends BaseApplication { } } - /** Reads API version information from api-versions.xml */ - public CompletableFuture getApiInformation() { - return CompletableFuture.supplyAsync( - () -> { - if (sdkInfo == null) { - try { - sdkInfo = new SDKInfo(IDEApplication.this); - } catch (Throwable th) { - LOG.error(getString(R.string.err_init_sdkinfo), th); - } - } - - return sdkInfo; - }); - } - - public CompletableFuture createInflaterConfig( - ILayoutInflater.ContextProvider contextProvider, Set resDirs) { - - return CompletableFuture.supplyAsync( - () -> { - try { - final var sdkInfo = getApiInformation().get(); - return new LayoutInflaterConfiguration.Builder() - .setAttrInfo(sdkInfo.getAttrInfo()) - .setWidgetInfo(sdkInfo.getWidgetInfo()) - .setResourceFinder(getResourceTable()) - .setResourceDirectories(resDirs) - .setContextProvider(contextProvider) - .create(); - } catch (Throwable e) { - throw new CompletionException(e); - } - }); - } - - public IResourceTable getResourceTable() { - return mResTable == null ? mResTable = new ProjectResourceTable() : mResTable; - } - - public AttrInfo attrInfo() { - return sdkInfo.getAttrInfo(); - } - - public WidgetInfo widgetInfo() { - return sdkInfo.getWidgetInfo(); - } - public void showChangelog() { final var intent = new Intent(Intent.ACTION_VIEW); intent.setData( diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java deleted file mode 100644 index cb52a09bf..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.fragments.attr; - -import android.text.Editable; -import android.text.TextUtils; -import android.widget.AutoCompleteTextView; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.ThreadUtils; -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.adapters.AttrValueCompletionAdapter; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.androidide.utils.TextWatcherAdapter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - -/** - * @author Akash Yadav - */ -public abstract class AbstractReferenceEditor extends BaseValueEditorFragment { - - private static final ILogger LOG = ILogger.newInstance("ReferenceEditor"); - public TextWatcherAdapter resInputWatcher; - - protected void setupReferenceInput(MaterialAutoCompleteTextView referenceInput) { - Objects.requireNonNull(referenceInput); - - referenceInput.setText(attribute.getValue()); - - if (resInputWatcher != null) { - referenceInput.removeTextChangedListener(resInputWatcher); - } - - resInputWatcher = - new TextWatcherAdapter() { - @Override - public void afterTextChanged(@NonNull Editable s) { - final var text = s.toString().trim(); - if (TextUtils.isEmpty(text)) { - return; - } - - notifyValueChanged(text); - } - }; - - referenceInput.addTextChangedListener(resInputWatcher); - - CompletableFuture.supplyAsync(this::computeReferenceItems) - .whenComplete((list, error) -> handleAutoCompleteResult(list, error, referenceInput)); - } - - @Override - protected void notifyValueChanged(@NonNull String newValue) { - try { - super.notifyValueChanged(newValue); - } catch (Throwable e) { - LOG.error("Unable to update resource value to '" + newValue + "'", e); - } - } - - protected void handleAutoCompleteResult( - List list, Throwable error, AutoCompleteTextView referenceInput) { - if (list == null || list.isEmpty()) { - LOG.error("No completion items found"); - LOG.error("Error was:", error); - return; - } - - LOG.debug("Found", list.size(), "resource items for completion"); - - ThreadUtils.runOnUiThread( - () -> { - final var adapter = new AttrValueCompletionAdapter(requireContext(), list); - referenceInput.setThreshold(1); - referenceInput.setAdapter(adapter); - adapter.notifyDataSetChanged(); - }); - } - - protected List computeReferenceItems() { - return new ArrayList<>(); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java deleted file mode 100644 index 573472717..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/BaseValueEditorFragment.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.widget.NestedScrollView; -import androidx.fragment.app.Fragment; - -import com.blankj.utilcode.util.SizeUtils; -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.models.XMLAttribute; -import com.itsaky.inflater.IAttribute; - -import java.util.Objects; - -/** - * Base class for value editor fragment. - * - * @author Akash Yadav - */ -public abstract class BaseValueEditorFragment extends Fragment { - - public static final String KEY_ATTR = "editor_attr"; - public static final String KEY_NAME = "editor_name"; - protected OnValueChangeListener mValueChangeListener; - protected XMLAttribute attribute; - protected String name; - - protected NestedScrollView scrollView; - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mValueChangeListener = (OnValueChangeListener) getParentFragment(); - } - - @Nullable - @Override - public View onCreateView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - if (scrollView != null) { - return scrollView; - } - - scrollView = new NestedScrollView(inflater.getContext()); - scrollView.setFillViewport(true); - scrollView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1)); - - final var child = createView(inflater, scrollView, savedInstanceState); - if (child != null) { - scrollView.addView(child); - } - - return scrollView; - } - - @Nullable - protected abstract View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState); - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - final var args = requireArguments(); - this.attribute = args.getParcelable(KEY_ATTR); - this.name = args.getString(KEY_NAME); - } - - protected void notifyValueChanged(@NonNull String newValue) { - Objects.requireNonNull(newValue); - - if (mValueChangeListener != null) { - mValueChangeListener.onValueChanged(this.attribute, newValue); - } - } - - /** Listener for listening for value change event. */ - public interface OnValueChangeListener { - /** - * Called when the value was changed. - * - * @param attribute The attribute whose value was changed. - * @param newValue The new value for the editor. - */ - void onValueChanged(IAttribute attribute, String newValue); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/BooleanEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/BooleanEditor.java deleted file mode 100644 index 8b1be8871..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/BooleanEditor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.RadioButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.databinding.LayoutBooleanAttrEditorBinding; -import com.itsaky.inflater.values.FrameworkValues; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Akash Yadav - */ -public class BooleanEditor extends AbstractReferenceEditor { - - private LayoutBooleanAttrEditorBinding binding; - - @Override - protected @Nullable View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.binding = LayoutBooleanAttrEditorBinding.inflate(inflater, container, false); - return this.binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - this.binding.choiceGroup.setOnCheckedChangeListener( - (group, checkedId) -> { - final var checked = (RadioButton) group.findViewById(checkedId); - notifyValueChanged(checked.getText().toString().trim()); - }); - - if (!TextUtils.isEmpty(attribute.getValue())) { - switch (attribute.getValue()) { - case "true": - this.binding.boolTrue.setChecked(true); - break; - case "false": - this.binding.boolFalse.setChecked(true); - } - } - - setupReferenceInput((MaterialAutoCompleteTextView) this.binding.booleanResInput.getEditText()); - } - - @Override - protected List computeReferenceItems() { - final var list = new ArrayList(); - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var dimens = entry.getValue().getTable("bool"); - if (dimens != null) { - list.addAll(dimens.keySet().stream().map("@bool/"::concat).collect(Collectors.toSet())); - } - } - - list.addAll( - FrameworkValues.listBools().stream() - .map("@android:bool/"::concat) - .collect(Collectors.toList())); - return list; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java deleted file mode 100644 index 1c57dc983..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.app.Dialog; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.app.IDEApplication; -import com.itsaky.androidide.colorpicker.ColorPickerView; -import com.itsaky.androidide.databinding.LayoutColorAttrEditorBinding; -import com.itsaky.androidide.utils.DialogUtils; -import com.itsaky.inflater.util.CommonParseUtils; -import com.itsaky.inflater.values.FrameworkValues; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Akash Yadav - */ -public class ColorEditor extends AbstractReferenceEditor { - - private LayoutColorAttrEditorBinding binding; - private Dialog colorPickerDialog; - - @Override - protected @Nullable View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.binding = LayoutColorAttrEditorBinding.inflate(inflater, container, false); - return this.binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - this.binding.pickColor.setOnClickListener(v -> getColorPickerDialog().show()); - setupReferenceInput((MaterialAutoCompleteTextView) this.binding.colorResInput.getEditText()); - - try { - CommonParseUtils parser = - new CommonParseUtils( - IDEApplication.getInstance().getResourceTable(), - IDEApplication.getInstance().getApplicationContext().getResources().getDisplayMetrics()); - final var col = parser.parseColor(attribute.getValue(), requireContext()); - this.binding.colorPreview.setCardBackgroundColor(col); - } catch (Throwable th) { - // ignored - } - } - - private Dialog getColorPickerDialog() { - - if (colorPickerDialog == null) { - final var builder = DialogUtils.newMaterialDialogBuilder(requireContext()); - ColorPickerView colorPickerView = new ColorPickerView(requireContext()); - - builder.setView(colorPickerView); - - colorPickerView.setOnPickListener( - (color, hexCode) -> { - if (colorPickerDialog != null) { - colorPickerDialog.dismiss(); - } - - this.binding.colorPreview.setCardBackgroundColor(color); - notifyValueChanged(hexCode); - }); - - colorPickerDialog = builder.create(); - } - - return colorPickerDialog; - } - - @Override - protected List computeReferenceItems() { - final var list = new ArrayList(); - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var dimens = entry.getValue().getTable("color"); - if (dimens != null) { - list.addAll(dimens.keySet().stream().map("@color/"::concat).collect(Collectors.toSet())); - } - } - - list.addAll( - FrameworkValues.listColors().stream() - .map("@android:color/"::concat) - .collect(Collectors.toList())); - - final var resTable = IDEApplication.getInstance().getResourceTable(); - list.addAll(resTable.listResourceNames("color")); - - return list; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/DimensionEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/DimensionEditor.java deleted file mode 100644 index 9c33c9313..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/DimensionEditor.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.databinding.LayoutDimensionAttrEditorBinding; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.androidide.utils.TextWatcherAdapter; -import com.itsaky.inflater.values.FrameworkValues; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * @author Akash Yadav - */ -public class DimensionEditor extends AbstractReferenceEditor { - - private static final ILogger LOG = ILogger.newInstance("DimensionEditor"); - public TextWatcherAdapter dimensionInputWatcher; - - private LayoutDimensionAttrEditorBinding binding; - private String[] dimensionUnits; - - @Override - protected @Nullable View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.binding = LayoutDimensionAttrEditorBinding.inflate(inflater, container, false); - return this.binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - final var dimensionInput = Objects.requireNonNull(this.binding.dimensionInput.getEditText()); - setDimensionValue(dimensionInput); - if (dimensionInputWatcher != null) { - dimensionInput.removeTextChangedListener(dimensionInputWatcher); - } - - dimensionInputWatcher = - new TextWatcherAdapter() { - @Override - public void afterTextChanged(@NonNull Editable s) { - final var text = s.toString().trim(); - final var unit = getDimensionUnits()[binding.unitSelector.getSelectedItemPosition()]; - - // This will call CommonParseUtils#parseDimension - notifyValueChanged(text.concat(unit)); - } - }; - - dimensionInput.addTextChangedListener(dimensionInputWatcher); - - final var dimensionResInput = - (MaterialAutoCompleteTextView) - Objects.requireNonNull(binding.dimensionResInput.getEditText()); - setupReferenceInput(dimensionResInput); - } - - private void setDimensionValue(EditText dimensionInput) { - final var val = attribute.getValue(); - if (TextUtils.isEmpty(val)) { - return; - } - - var dimension = val.substring(0, val.length() - 2); - if (TextUtils.isDigitsOnly(dimension)) { - dimensionInput.setText(dimension); - final var arr = requireContext().getResources().getStringArray(R.array.dimension_units); - final var index = Arrays.asList(arr).indexOf(val.substring(dimension.length())); - if (index >= 0) { - this.binding.unitSelector.setSelection(index); - } - } - } - - @NonNull - private String[] getDimensionUnits() { - if (dimensionUnits == null) { - dimensionUnits = requireContext().getResources().getStringArray(R.array.dimension_units); - } - - return dimensionUnits; - } - - @Override - protected List computeReferenceItems() { - final var list = new ArrayList(); - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var dimens = entry.getValue().getTable("dimen"); - if (dimens != null) { - list.addAll(dimens.keySet().stream().map("@dimen/"::concat).collect(Collectors.toSet())); - } - } - - list.addAll( - FrameworkValues.listDimens().stream() - .map("@android:dimen/"::concat) - .collect(Collectors.toList())); - return list; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/EnumEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/EnumEditor.java deleted file mode 100644 index 46259bfe9..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/EnumEditor.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.text.TextUtils; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.chip.Chip; -import com.google.android.material.chip.ChipGroup; -import com.itsaky.androidide.utils.ILogger; - -/** - * @author Akash Yadav - */ -public class EnumEditor extends FixedValueEditor { - - private static final ILogger LOG = ILogger.newInstance("EnumEditor"); - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - this.chipGroup.setSingleSelection(true); - this.chipGroup.setSelectionRequired(true); - - final var val = this.attribute.getValue(); - if (!TextUtils.isEmpty(val)) { - for (int i = 0; i < this.chipGroup.getChildCount(); i++) { - final var chip = (Chip) this.chipGroup.getChildAt(i); - if (val.equals(chip.getText().toString().trim())) { - chip.setChecked(true); - break; - } - } - } - } - - @Override - protected void onCheckChanged(@NonNull ChipGroup group, int checkedId) { - final var chip = (Chip) group.findViewById(group.getCheckedChipId()); - if (chip == null) { - LOG.error("Unable to update enum value. Checked Chip view is null."); - return; - } - - final var val = chip.getText().toString().trim(); - notifyValueChanged(val); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FixedValueEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FixedValueEditor.java deleted file mode 100644 index 846c4739b..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FixedValueEditor.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.fragments.attr; - -import android.content.res.ColorStateList; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; - -import com.google.android.material.chip.Chip; -import com.google.android.material.chip.ChipGroup; -import com.itsaky.androidide.resources.R; - -import java.util.Objects; - -/** - * @author Akash Yadav - */ -public abstract class FixedValueEditor extends BaseValueEditorFragment { - - protected ChipGroup chipGroup; - - @Nullable - @Override - protected View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.chipGroup = new ChipGroup(inflater.getContext()); - this.chipGroup.setLayoutParams( - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - this.chipGroup.setSelectionRequired(true); - this.chipGroup.setSingleSelection(false); - - return this.chipGroup; - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - Objects.requireNonNull(this.attribute); - Objects.requireNonNull(this.chipGroup); - - this.chipGroup.setSingleSelection(false); - this.chipGroup.setSelectionRequired(true); - - final var attr = this.attribute.getAttr(); - for (var value : attr.possibleValues) { - if (TextUtils.isEmpty(value)) { - continue; - } - - this.chipGroup.addView(newChip(value, value.equals(this.attribute.getValue()))); - } - - this.chipGroup.setOnCheckedChangeListener(this::onCheckChanged); - } - - protected abstract void onCheckChanged(@NonNull ChipGroup group, int checkedId); - - protected Chip newChip(String title, boolean checked) { - final var chip = new Chip(requireContext()); - chip.setId(View.generateViewId()); - chip.setText(title); - chip.setCheckedIcon(ContextCompat.getDrawable(requireContext(), R.drawable.ic_ok)); - chip.setChecked(checked); - chip.setChipBackgroundColor( - ContextCompat.getColorStateList(requireContext(), R.color.bg_enum_chips)); - chip.setTextColor(ContextCompat.getColor(requireContext(), R.color.primaryTextColor)); - chip.setLayoutParams( - new ChipGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - chip.setChipStrokeWidth(1f); - chip.setChipStrokeColor( - ColorStateList.valueOf(ContextCompat.getColor(requireContext(), R.color.primaryDarkColor))); - - return chip; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java deleted file mode 100644 index 344fabc61..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.chip.Chip; -import com.google.android.material.chip.ChipGroup; -import com.itsaky.androidide.utils.ILogger; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * @author Akash Yadav - */ -public class FlagEditor extends FixedValueEditor { - - private static final ILogger LOG = ILogger.newInstance("FlagEditor"); - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - this.chipGroup.setSingleSelection(false); - this.chipGroup.setSelectionRequired(true); - this.chipGroup.setOnCheckedChangeListener(null); // does not work for multiple selections - - if (!TextUtils.isEmpty(attribute.getValue())) { - final var values = Arrays.asList(this.attribute.getValue().split("\\|")); - for (int i = 0; i < this.chipGroup.getChildCount(); i++) { - final var chip = (Chip) this.chipGroup.getChildAt(i); - if (values.contains(chip.getText().toString().trim())) { - chip.setChecked(true); - } - } - } - } - - @Override - protected void onCheckChanged(@NonNull ChipGroup group, int checkedId) { - // Does noting... - } - - @Override - protected Chip newChip(String title, boolean checked) { - final var chip = super.newChip(title, checked); - chip.setOnCheckedChangeListener(this::onCheckChangedMultiple); - return chip; - } - - protected void onCheckChangedMultiple(CompoundButton compoundButton, boolean b) { - final var items = new ArrayList(); - for (var id : this.chipGroup.getCheckedChipIds()) { - final var chip = (Chip) this.chipGroup.findViewById(id); - if (chip == null) { - LOG.error("Unable to find chip with checked id:", id); - continue; - } - - final var val = chip.getText().toString().trim(); - items.add(val); - } - - notifyValueChanged(TextUtils.join("|", items)); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FloatEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FloatEditor.java deleted file mode 100644 index 2c02f1926..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FloatEditor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -/** - * @author Akash Yadav - */ -public class FloatEditor extends UnimplementedEditorFragment {} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FractionEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FractionEditor.java deleted file mode 100644 index 229379db0..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FractionEditor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -/** - * @author Akash Yadav - */ -public class FractionEditor extends UnimplementedEditorFragment {} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/IntegerEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/IntegerEditor.java deleted file mode 100644 index 69ea04bf9..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/IntegerEditor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -/** - * @author Akash Yadav - */ -public class IntegerEditor extends UnimplementedEditorFragment {} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/ReferenceEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/ReferenceEditor.java deleted file mode 100644 index a5d54f820..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/ReferenceEditor.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.app.IDEApplication; -import com.itsaky.androidide.databinding.LayoutReferenceAttrEditorBinding; -import com.itsaky.inflater.values.FrameworkValues; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author Akash Yadav - */ -public class ReferenceEditor extends AbstractReferenceEditor { - - private LayoutReferenceAttrEditorBinding binding; - - @Nullable - @Override - protected View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.binding = LayoutReferenceAttrEditorBinding.inflate(inflater, container, false); - return this.binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - setupReferenceInput((MaterialAutoCompleteTextView) this.binding.resInput.getEditText()); - } - - @Override - protected List computeReferenceItems() { - final var resTable = IDEApplication.getInstance().getResourceTable(); - final var list = new ArrayList<>(resTable.listResourceNames(null)); - - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var resourceMap = entry.getValue().getResourceMap(); - for (var resourceEntries : resourceMap.entrySet()) { - final var name = resourceEntries.getKey(); - list.addAll( - resourceEntries.getValue().keySet().stream() - .map(("@android:" + name + "/")::concat) - .collect(Collectors.toList())); - } - } - - list.addAll(FrameworkValues.listAllResources()); - - return list; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/StringEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/StringEditor.java deleted file mode 100644 index ad86ea692..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/StringEditor.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.fragments.attr; - -import android.os.Bundle; -import android.text.Editable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import com.itsaky.androidide.databinding.LayoutStringAttrEditorBinding; -import com.itsaky.androidide.utils.TextWatcherAdapter; -import com.itsaky.inflater.values.FrameworkValues; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * @author Akash Yadav - */ -public class StringEditor extends AbstractReferenceEditor { - - private LayoutStringAttrEditorBinding binding; - - @Nullable - @Override - protected View createView( - @NonNull final LayoutInflater inflater, - @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - this.binding = LayoutStringAttrEditorBinding.inflate(inflater, container, false); - return this.binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - final var stringInput = Objects.requireNonNull(this.binding.stringInput.getEditText()); - stringInput.addTextChangedListener( - new TextWatcherAdapter() { - @Override - public void afterTextChanged(@NonNull Editable s) { - notifyValueChanged(s.toString()); - } - }); - - stringInput.setText(this.attribute.getValue()); - setupReferenceInput((MaterialAutoCompleteTextView) this.binding.stringResInput.getEditText()); - } - - @Override - protected List computeReferenceItems() { - final var list = new ArrayList(); - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var dimens = entry.getValue().getTable("string"); - if (dimens != null) { - list.addAll(dimens.keySet().stream().map("@string/"::concat).collect(Collectors.toSet())); - } - } - - list.addAll( - FrameworkValues.listStrings().stream() - .map("@android:string/"::concat) - .collect(Collectors.toList())); - return list; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/UnimplementedEditorFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/attr/UnimplementedEditorFragment.kt deleted file mode 100644 index 411edb963..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/UnimplementedEditorFragment.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.attr - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -/** @author Akash Yadav */ -open class UnimplementedEditorFragment : BaseValueEditorFragment() { - override fun createView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return super.onCreateView(inflater, container, savedInstanceState) - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java deleted file mode 100644 index bf03223f2..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.sheets; - -import static com.itsaky.androidide.resources.R.drawable; -import static com.itsaky.androidide.resources.R.string; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.transition.TransitionManager; - -import com.google.android.material.bottomsheet.BottomSheetDialogFragment; -import com.itsaky.androidide.adapters.SimpleIconTextAdapter; -import com.itsaky.androidide.adapters.XMLAttributeListAdapter; -import com.itsaky.androidide.app.IDEApplication; -import com.itsaky.androidide.databinding.LayoutAttrEditorSheetBinding; -import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding; -import com.itsaky.androidide.fragments.attr.BaseValueEditorFragment; -import com.itsaky.androidide.models.IconTextListItem; -import com.itsaky.androidide.models.XMLAttribute; -import com.itsaky.androidide.utils.DialogUtils; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.attrinfo.models.Attr; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IView; -import com.itsaky.inflater.impl.UiAttribute; -import com.itsaky.toaster.Toaster; -import com.itsaky.toaster.ToastUtilsKt; - -import org.jetbrains.annotations.Contract; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.TreeSet; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -public class AttrEditorSheet extends BottomSheetDialogFragment - implements SimpleIconTextAdapter.OnBindListener, - Consumer, - BaseValueEditorFragment.OnValueChangeListener { - - private static final List VIEW_ACTIONS = new ArrayList<>(); - private static final ILogger LOG = ILogger.newInstance("AttrBottomSheet"); - private AttributeListSheet mAttrListSheet; - private AttrValueEditorSheet mValueEditorSheet; - private IView selectedView; - - @SuppressWarnings({"FieldCanBeLocal", "unused"}) - private File layout; - - private LayoutAttrEditorSheetBinding binding; - private OnViewDeletionFailedListener mDeletionFailedListener; - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - this.binding = - LayoutAttrEditorSheetBinding.inflate(LayoutInflater.from(getContext()), container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - if (VIEW_ACTIONS.isEmpty()) { - Collections.addAll( - VIEW_ACTIONS, - IconTextListItem.create(getString(string.msg_viewaction_add_attr), drawable.ic_add), - IconTextListItem.create(getString(string.title_viewaction_delete), drawable.ic_delete), - IconTextListItem.create( - getString(string.msg_viewaction_select_parent), drawable.ic_view_select_parent)); - } - - setupViewData(); - } - - private void setupViewData() { - binding.actionsList.setAdapter(new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this)); - - if (dismissIfSelectedNull()) { - LOG.error("Cannot edit attributes of a null view."); - return; - } - - binding.widgetName.setText(this.selectedView.getXmlTag()); - binding.attrList.setAdapter( - new XMLAttributeListAdapter( - this.selectedView.getAttributes().stream() - .map(XMLAttribute::new) - .collect(Collectors.toList()), - this::onAttrClick)); - } - - private void onAttrClick( - LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) { - - if (dismissIfSelectedNull()) { - LOG.error("Cannot edit attributes of a null view."); - return; - } - - final var format = attribute.findFormat(); - if (format == -1) { - ToastUtilsKt.toast(getString(string.msg_no_attr_format), Toaster.Type.ERROR); - LOG.error(getString(string.msg_no_attr_format), attribute); - return; - } - - showValueEditorSheet(attribute); - } - - private void showValueEditorSheet(@NonNull XMLAttribute attribute) { - getValueEditorSheet(attribute).show(getChildFragmentManager(), "attr_value_editor_sheet"); - } - - @NonNull - public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) { - - if (mValueEditorSheet == null) { - mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute); - } - - mValueEditorSheet.setAttribute(attribute); - return mValueEditorSheet; - } - - private boolean dismissIfSelectedNull() { - if (selectedView == null) { - dismiss(); - return true; - } - return false; - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mDeletionFailedListener = (OnViewDeletionFailedListener) context; - } - - public AttrEditorSheet setLayout(File layout) { - Objects.requireNonNull(layout); - - this.layout = layout; - return this; - } - - public void setSelectedView(IView view) { - this.selectedView = view; - } - - @Override - public void postBind( - IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) { - final var binding = holder.binding; - binding.getRoot().setOnClickListener(v -> onViewActionClick(position)); - } - - private void onViewActionClick(int position) { - if (dismissIfSelectedNull()) { - return; - } - - if (position == 0) { // Add attribute - var tag = this.selectedView.getXmlTag(); - if ("include".equals(tag) || "merge".equals(tag)) { - tag = "View"; - } - - final var attrs = IDEApplication.getInstance().attrInfo(); - final var style = attrs.getStyle(tag); - if (style == null) { - LOG.error("Unable to retrieve attributes for tag:", tag); - return; - } - - final var attributes = new TreeSet(Comparator.comparing(attr -> attr.name)); - attributes.addAll(style.attributes); - attributes.addAll(attrs.NO_PARENT.attributes); - - final var widgetInfo = IDEApplication.getInstance().widgetInfo(); - final var widget = widgetInfo.getWidgetBySimpleName(tag); - if (widget != null) { - for (var superclass : widget.superclasses) { - if ("java.lang.Object".equals(superclass)) { - break; - } - - final var simpleName = superclass.substring(superclass.lastIndexOf(".") + 1); - final var superStyle = attrs.getStyle(simpleName); - if (superStyle != null) { - attributes.addAll(superStyle.attributes); - } - } - } - - final var parent = this.selectedView.getParent(); - if (parent != null) { - final var parentTag = parent.getXmlTag(); - final var parentWidget = widgetInfo.getWidgetBySimpleName(parentTag); - if (parentWidget != null) { - final var parentLayoutParams = attrs.getStyle(parentTag + "_Layout"); - if (parentLayoutParams != null) { - attributes.addAll(parentLayoutParams.attributes); - } - - final var paramSuperclasses = widgetInfo.getLayoutParamSuperClasses(parentWidget.name); - if (paramSuperclasses != null) { - for (var superclass : paramSuperclasses) { - if ("java.lang.Object".equals(superclass)) { - continue; - } - - final var split = superclass.split("\\."); - final var simpleClassName = split[split.length - 2]; - var paramName = split[split.length - 1]; - paramName = paramName.substring(0, paramName.length() - "Params".length()); - final var superParamEntry = attrs.getStyle(simpleClassName + "_" + paramName); - if (superParamEntry != null) { - attributes.addAll(superParamEntry.attributes); - } - } - } - } - } - - final var sheet = getAttrListSheet(); - sheet.setItems(filterAppliedAttributes(attributes)); - sheet.show(getChildFragmentManager(), "attr_list_sheet"); - - } else if (position == 1) { // Delete - DialogUtils.newYesNoDialog( - getContext(), - (dialog, which) -> { - var handled = selectedView.removeFromParent(); - if (!handled) { - handled = - mDeletionFailedListener != null - && mDeletionFailedListener.onDeletionFailed(this.selectedView); - } - if (!handled) { - ToastUtilsKt.toast(getString(string.msg_view_deletion_failed), Toaster.Type.ERROR); - } else { - dismiss(); - } - }, - (dialog, which) -> dialog.dismiss()) - .show(); - } else if (position == 2) { // Select parent - if (this.selectedView.getParent() == null) { - ToastUtilsKt.toast(getString(string.msg_no_view_parent), Toaster.Type.ERROR); - return; - } - - this.selectedView = this.selectedView.getParent(); - - TransitionManager.beginDelayedTransition(binding.getRoot()); - setupViewData(); - } - } - - @NonNull - @Contract("_ -> new") - private List filterAppliedAttributes(@NonNull TreeSet attributes) { - attributes.removeIf(attr -> this.selectedView.hasAttribute(attr.namespace, attr.name)); - return new ArrayList<>(attributes); - } - - @NonNull - private AttributeListSheet getAttrListSheet() { - if (mAttrListSheet == null) { - mAttrListSheet = new AttributeListSheet(); - mAttrListSheet.setCancelable(true); - } - - return mAttrListSheet; - } - - @Override - public void accept(Attr attr) { - addNewAttribute(attr); - } - - private void addNewAttribute(@NonNull Attr attr) { - final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false); - attribute.setAttr(attr); - showValueEditorSheet(attribute); - } - - @Override - public void onValueChanged(@NonNull IAttribute attribute, String newValue) { - if (dismissIfSelectedNull()) { - LOG.warn("Cannot update attribute with new value. Selected view is null."); - return; - } - - final var attributeName = attribute.getAttributeName(); - final var namespace = attribute.getNamespace(); - - if (this.selectedView.hasAttribute(namespace, attributeName)) { - this.selectedView.updateAttribute(namespace, attributeName, newValue); - } else { - final var attr = new UiAttribute(namespace, attributeName, newValue); - this.selectedView.addAttribute(attr); - } - } - - /** - * A listener can be used to get notified when we fail to remove a view from its parent. This is - * used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}. - * - *

When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet - * fails to remove the view from its parent ({@link IView#getParent()} is null for root XML - * layout). In this case, DesignerActivity check if the view that we were trying to delete is the - * root layout or not. If it is the root layout, then it deletes the layout from the layout - * container. - * - * @author Akash Yadav - */ - public interface OnViewDeletionFailedListener { - - /** - * @param view The view that was not removed from its parent. - * @return {@code true} if the listener handled the error. {@code false} otherwise. - */ - boolean onDeletionFailed(IView view); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java deleted file mode 100644 index 04ee44b01..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrValueEditorSheet.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.fragments.sheets; - -import static com.itsaky.attrinfo.models.Attr.FORMAT_BOOLEAN; -import static com.itsaky.attrinfo.models.Attr.FORMAT_COLOR; -import static com.itsaky.attrinfo.models.Attr.FORMAT_DIMENSION; -import static com.itsaky.attrinfo.models.Attr.FORMAT_ENUM; -import static com.itsaky.attrinfo.models.Attr.FORMAT_FLAG; -import static com.itsaky.attrinfo.models.Attr.FORMAT_FLOAT; -import static com.itsaky.attrinfo.models.Attr.FORMAT_FRACTION; -import static com.itsaky.attrinfo.models.Attr.FORMAT_INTEGER; -import static com.itsaky.attrinfo.models.Attr.FORMAT_REFERENCE; -import static com.itsaky.attrinfo.models.Attr.FORMAT_STRING; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.bottomsheet.BottomSheetDialogFragment; -import com.itsaky.androidide.adapters.AttrValueFormatTabAdapter; -import com.itsaky.androidide.databinding.LayoutAttrValueEditorBinding; -import com.itsaky.androidide.fragments.attr.BaseValueEditorFragment; -import com.itsaky.androidide.fragments.attr.BooleanEditor; -import com.itsaky.androidide.fragments.attr.ColorEditor; -import com.itsaky.androidide.fragments.attr.DimensionEditor; -import com.itsaky.androidide.fragments.attr.EnumEditor; -import com.itsaky.androidide.fragments.attr.FlagEditor; -import com.itsaky.androidide.fragments.attr.FloatEditor; -import com.itsaky.androidide.fragments.attr.FractionEditor; -import com.itsaky.androidide.fragments.attr.IntegerEditor; -import com.itsaky.androidide.fragments.attr.ReferenceEditor; -import com.itsaky.androidide.fragments.attr.StringEditor; -import com.itsaky.androidide.models.XMLAttribute; -import com.itsaky.attrinfo.models.Attr; -import com.itsaky.inflater.IAttribute; - -import org.jetbrains.annotations.Contract; - -import java.util.Objects; - -/** - * Sheet for editing value of an attribute. - * - * @author Akash Yadav - */ -public class AttrValueEditorSheet extends BottomSheetDialogFragment - implements BaseValueEditorFragment.OnValueChangeListener { - - private static final String KEY_ATTRIBUTE = "attrEditorSheet_attribute"; - private BaseValueEditorFragment.OnValueChangeListener valueChangeListener; - private LayoutAttrValueEditorBinding binding; - private AttrValueFormatTabAdapter adapter; - private XMLAttribute attribute; - - @NonNull - public static AttrValueEditorSheet newInstance(final XMLAttribute attribute) { - Objects.requireNonNull(attribute); - - final var args = new Bundle(); - args.putParcelable(KEY_ATTRIBUTE, attribute); - - final var fragment = new AttrValueEditorSheet(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - this.valueChangeListener = (BaseValueEditorFragment.OnValueChangeListener) getParentFragment(); - } - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - this.binding = LayoutAttrValueEditorBinding.inflate(inflater, container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - if (attribute == null) { - this.attribute = requireArguments().getParcelable(KEY_ATTRIBUTE); - } - - Objects.requireNonNull(this.attribute); - - final var namespace = this.attribute.getNamespace(); - final var formatText = Attr.createFormatText(attribute.getFormat()); - this.binding.attributeName.setText( - String.format("%s:%s", namespace.getPrefix(), attribute.getAttributeName())); - this.binding.attributeFormat.setText(formatText); - - setupTabs(formatText); - } - - @SuppressLint("NotifyDataSetChanged") - private void setupTabs(@NonNull String formatText) { - this.binding.tabs.removeAllTabs(); - this.binding.pager.setSaveEnabled(false); - - if (this.adapter != null) { - this.adapter.removeAll(); - this.adapter.notifyDataSetChanged(); - } - - this.adapter = new AttrValueFormatTabAdapter(getChildFragmentManager(), attribute); - - final var formats = formatText.split("\\|"); - for (var formatName : formats) { - if (TextUtils.isEmpty(formatName) || formatName.length() < 2) { - continue; - } - - final var frag = getFragmentClass(formatName); - if (frag != null) { - this.adapter.addFragment(frag, formatName); - } - } - - binding.pager.setAdapter(this.adapter); - binding.tabs.setupWithViewPager(binding.pager); - } - - @Nullable - @Contract(pure = true) - private Class getFragmentClass(@NonNull String name) { - switch (name) { - case FORMAT_REFERENCE: - return ReferenceEditor.class; - case FORMAT_COLOR: - return ColorEditor.class; - case FORMAT_BOOLEAN: - return BooleanEditor.class; - case FORMAT_DIMENSION: - return DimensionEditor.class; - case FORMAT_FLOAT: - return FloatEditor.class; - case FORMAT_INTEGER: - return IntegerEditor.class; - case FORMAT_FRACTION: - return FractionEditor.class; - case FORMAT_STRING: - return StringEditor.class; - case FORMAT_ENUM: - return EnumEditor.class; - case FORMAT_FLAG: - return FlagEditor.class; - } - - return null; - } - - public void setAttribute(XMLAttribute attribute) { - Objects.requireNonNull(attribute); - this.attribute = attribute; - } - - @Override - public void onValueChanged(IAttribute attribute, String newValue) { - if (valueChangeListener != null) { - valueChangeListener.onValueChanged(attribute, newValue); - } - } -} diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java deleted file mode 100644 index f6a8e6c66..000000000 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.fragments.sheets; - -import android.content.Context; -import android.os.Bundle; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.SearchView; -import androidx.fragment.app.DialogFragment; - -import com.google.android.material.bottomsheet.BottomSheetDialogFragment; -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.adapters.AttrListAdapter; -import com.itsaky.androidide.databinding.LayoutAddAttrSheetBinding; -import com.itsaky.attrinfo.models.Attr; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -/** - * A bottom sheet dialog for showing a simple list of text icon pair. - * - * @author Akash Yadav - * @see com.itsaky.androidide.models.IconTextListItem - */ -public class AttributeListSheet extends BottomSheetDialogFragment { - - private LayoutAddAttrSheetBinding binding; - private Consumer clickConsumer; - private List mItems; - - @Nullable - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - this.binding = LayoutAddAttrSheetBinding.inflate(inflater, container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - binding.searchBar.setOnQueryTextListener( - new SearchView.OnQueryTextListener() { - - @Override - public boolean onQueryTextSubmit(String query) { - return filterAttributes(query.trim()); - } - - @Override - public boolean onQueryTextChange(String newText) { - return filterAttributes(newText.trim()); - } - }); - binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, mItems)); - } - - private boolean filterAttributes(@NonNull String query) { - if (mItems == null || TextUtils.isEmpty(query)) { - update(); - return true; - } - - final var filtered = - this.mItems.stream() - .filter( - attr -> attr.name.toLowerCase(Locale.ROOT).contains(query.toLowerCase(Locale.ROOT))) - .collect(Collectors.toList()); - - update(filtered); - return true; - } - - public void update() { - update(mItems); - } - - public void update(List items) { - if (binding != null) { - binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items)); - } - } - - public void setItems(@Nullable List items) { - if (items == null) { - items = new ArrayList<>(); - } - - mItems = items; - update(); - } - - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - //noinspection unchecked - this.clickConsumer = (Consumer) getParentFragment(); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.kt index 81ee3dbe1..0f620f173 100644 --- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.kt +++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.kt @@ -18,8 +18,8 @@ package com.itsaky.androidide.language.xml import android.graphics.Color +import android.graphics.Color.parseColor import com.google.common.collect.EvictingQueue -import com.itsaky.androidide.app.IDEApplication import com.itsaky.androidide.language.incremental.BaseIncrementalAnalyzeManager import com.itsaky.androidide.language.incremental.IncrementalToken import com.itsaky.androidide.language.incremental.LineState @@ -48,10 +48,10 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATOR import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_NORMAL import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.XML_TAG import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment -import com.itsaky.inflater.util.CommonParseUtils import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult import io.github.rosemoe.sora.lang.styling.Span import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle +import java.util.regex.Pattern /** * Syntax analyzer for XML. @@ -85,7 +85,7 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { override fun popTokensAfterIncomplete( incompleteToken: IncrementalToken, - tokens: MutableList + tokens: MutableList, ) { // Do nothing } @@ -93,8 +93,6 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { override fun generateSpans( tokens: LineTokenizeResult, ): MutableList { - val app = IDEApplication.getInstance() - val parser = CommonParseUtils(app.resourceTable, app.resources.displayMetrics) val spans = mutableListOf() var previous = XMLLexer.SEA_WS @@ -109,7 +107,7 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { COMMENT_START, COMMENT_END, CommentModeEnd, - CommentText -> spans.add(Span.obtain(offset, forComment())) + CommentText, -> spans.add(Span.obtain(offset, forComment())) OPEN, DASH, NOT, @@ -126,8 +124,7 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { val text: String = token.text val textVar = text.replace("\"", "") if (isColorValue(textVar)) { - val color: Int = - parser.parseColor(textVar, IDEApplication.getInstance().applicationContext) + val color: Int = parseColor(textVar) val span = Span.obtain(offset + 1, makeStyle(LITERAL)) span.setUnderlineColor(color) spans.add(span) @@ -163,8 +160,7 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { try { val textVar: String = token.text if (isColorValue(textVar)) { - val color: Int = - parser.parseColor(textVar, IDEApplication.getInstance().applicationContext) + val color: Int = parseColor(textVar) val span = Span.obtain(offset, makeStyle(TEXT_NORMAL)) span.setUnderlineColor(color) spans.add(span) @@ -187,8 +183,7 @@ class XMLAnalyzer : BaseIncrementalAnalyzeManager(XMLLexer::class.java) { override fun handleIncompleteToken(token: IncrementalToken) {} private fun isColorValue(value: String): Boolean { - return (value.startsWith("#") || - value.startsWith("@android:color") || - value.startsWith("@color/")) + return hexColorMatcher.matcher(value).matches() } + private val hexColorMatcher: Pattern = Pattern.compile("#[a-fA-F\\d]{6,8}") } diff --git a/app/src/main/java/com/itsaky/androidide/models/UIWidget.java b/app/src/main/java/com/itsaky/androidide/models/UIWidget.java deleted file mode 100755 index 4b38c34d7..000000000 --- a/app/src/main/java/com/itsaky/androidide/models/UIWidget.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.models; - -import android.os.Parcel; -import android.os.Parcelable; -import android.view.View; - -import androidx.annotation.DrawableRes; - -import java.util.Objects; - -/** - * Holds data about the widget item in widgets list shown in UI Designer screen. - * - * @author Akash Yadav - */ -public class UIWidget implements IconTextListItem, Parcelable { - - public static final Creator CREATOR = - new Creator() { - @Override - public UIWidget createFromParcel(Parcel in) { - return new UIWidget(in); - } - - @Override - public UIWidget[] newArray(int size) { - return new UIWidget[size]; - } - }; - private final String name; - private final int icon; - private final String clazz; - - private UIWidget(Parcel in) { - this.name = in.readString(); - this.icon = in.readInt(); - this.clazz = in.readString(); - } - - public UIWidget(String name, @DrawableRes int icon, Class clazz) { - this.name = name; - this.icon = icon; - this.clazz = clazz.getName(); - } - - @Override - public int hashCode() { - return Objects.hash(getName(), getIcon(), clazz); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UIWidget widget = (UIWidget) o; - return getIcon() == widget.getIcon() - && getName().equals(widget.getName()) - && clazz.equals(widget.clazz); - } - - public String getName() { - return name; - } - - public int getIcon() { - return icon; - } - - /** - * Get the class of this widget. - * - * @return The class that this widget represents - * @throws ClassNotFoundException If there is no class with this widget's class name. However, - * this will never be thrown. - */ - public Class asClass() throws ClassNotFoundException { - return getClass().getClassLoader().loadClass(this.clazz).asSubclass(View.class); - } - - public String getWidgetClassName() { - return this.clazz; - } - - @Override - public String getText() { - return getName(); - } - - @Override - public int getIconResource() { - return getIcon(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(name); - dest.writeInt(icon); - dest.writeString(clazz); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java b/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java deleted file mode 100755 index fa1fb3b0a..000000000 --- a/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.models; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.Contract; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Model class for holding data about a group that will be displayed in Widgets list in UI Designer - * screen. - * - *

A UIWidgetGroup contains multiple {@link UIWidget}. - * - * @author Akash Yadav - */ -public class UIWidgetGroup implements IconTextListItem, Parcelable { - - public static final Creator CREATOR = - new Creator() { - - @NonNull - @Contract("_ -> new") - @Override - public UIWidgetGroup createFromParcel(Parcel in) { - return new UIWidgetGroup(in); - } - - @NonNull - @Contract(value = "_ -> new", pure = true) - @Override - public UIWidgetGroup[] newArray(int size) { - return new UIWidgetGroup[size]; - } - }; - /** Name of this group. */ - private final String name; - /** Children of this group. */ - private final List children; - /** Is this group currently selected in the group list? */ - private boolean selected; - - public UIWidgetGroup(String name) { - this.name = name; - this.selected = false; - this.children = new ArrayList<>(); - } - - protected UIWidgetGroup(@NonNull Parcel in) { - this.name = in.readString(); - this.children = in.createTypedArrayList(UIWidget.CREATOR); - this.selected = in.readByte() != 0; - } - - /** - * Add this widget to this group. Does nothing if the child is already present. - * - * @param child The child to add. - * @return This instance for chained method calls. - */ - public UIWidgetGroup addChild(UIWidget child) { - if (children.contains(child)) { - return this; - } - this.children.add(child); - return this; - } - - /** - * Get the child at the given index. - * - * @param index The index of the child. - * @return The child at the index. - * @throws IndexOutOfBoundsException If the given index is not valid. - */ - public UIWidget getChild(int index) { - return children.get(index); - } - - @Override - public int hashCode() { - return Objects.hash(getName(), getChildren(), isSelected()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UIWidgetGroup group = (UIWidgetGroup) o; - return isSelected() == group.isSelected() - && getName().equals(group.getName()) - && getChildren().equals(group.getChildren()); - } - - /** - * Get the name of this group. - * - * @return The name of this group. - */ - public String getName() { - return name; - } - - /** - * Get the children of this group. - * - * @return The children. - */ - @NonNull - public List getChildren() { - return children; - } - - public boolean isSelected() { - return selected; - } - - public void setSelected(boolean selected) { - this.selected = selected; - } - - @Override - public String getText() { - return getName(); - } - - @Override - public int getIconResource() { - return -1; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(name); - dest.writeTypedList(children); - dest.writeByte((byte) (selected ? 1 : 0)); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java deleted file mode 100644 index 885538fc1..000000000 --- a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.models; - -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.app.IDEApplication; -import com.itsaky.attrinfo.models.Attr; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.impl.UiAttribute; -import com.itsaky.xml.INamespace; - -import org.jetbrains.annotations.Contract; - -import java.util.Objects; - -/** - * A model that is used to show attributes list of a view. - * - * @author Akash Yadav - */ -public class XMLAttribute extends UiAttribute implements Parcelable { - - public static final Creator CREATOR = - new Creator<>() { - @NonNull - @Contract("_ -> new") - @Override - public XMLAttribute createFromParcel(Parcel in) { - return new XMLAttribute(in); - } - - @NonNull - @Contract(value = "_ -> new", pure = true) - @Override - public XMLAttribute[] newArray(int size) { - return new XMLAttribute[size]; - } - }; - public Attr attr; - /** Is this attribute applied to any view? */ - private boolean isApplied; - /** - * Format of this attribute. May be stored here so we don't have to look in {@link - * com.itsaky.attrinfo.AttrInfo AttrInfo} again and again. - */ - private int format = -1; - - public XMLAttribute(IAttribute attribute) { - this(attribute, true); - } - - public XMLAttribute(@NonNull IAttribute attribute, boolean isApplied) { - this(attribute.getNamespace(), attribute.getAttributeName(), attribute.getValue(), isApplied); - } - - public XMLAttribute(INamespace namespace, String name, String value, boolean isApplied) { - super(namespace, name, value); - this.isApplied = isApplied; - } - - private XMLAttribute(@NonNull Parcel in) { - this( - in.readParcelable(INamespace.class.getClassLoader()), - in.readString(), - in.readString(), - in.readByte() != 0); - this.attr = in.readParcelable(Attr.class.getClassLoader()); - this.format = in.readInt(); - } - - /** - * Create a new attribute instance with the android namespace. {@link #isApplied()} will return - * true by default. - * - * @param name The name of the attribute. - * @param value The value of the attribute. - * @return A new instance of this class with the provided data. - */ - @NonNull - @Contract("_, _ -> new") - public static XMLAttribute newAndroidAttribute(String name, String value) { - return new XMLAttribute(INamespace.ANDROID, name, value, true); - } - - /** - * Create a new attribute instance with the android namespace. The {@link #isApplied()} will - * return {@code false} by default. - * - * @param name The name of the attribute. - * @return The new attribute instance. - */ - @NonNull - @Contract("_ -> new") - public static XMLAttribute newAndroidAttribute(String name) { - return new XMLAttribute(INamespace.ANDROID, name, "", false); - } - - public int findFormat() { - - if (getFormat() > 0) { - return getFormat(); - } - - final var attrs = IDEApplication.getInstance().attrInfo(); - this.attr = attrs.getAttribute(getAttributeName()); - if (this.attr != null) { - this.format = this.attr.format; - } - - return getFormat(); - } - - public int getFormat() { - return format; - } - - public void setFormat(int format) { - this.format = format; - } - - public boolean hasFormat(int format) { - return (this.format & format) != 0; - } - - @Override - public void apply(String value) { - super.apply(value); - isApplied = true; - } - - public boolean isApplied() { - return isApplied; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(getNamespace(), flags); - dest.writeString(getAttributeName()); - dest.writeString(getValue()); - dest.writeByte((byte) (isApplied ? 1 : 0)); - dest.writeParcelable(getAttr(), flags); - dest.writeInt(this.format); - } - - public Attr getAttr() { - return attr; - } - - public void setAttr(Attr attr) { - Objects.requireNonNull(attr); - this.attr = attr; - setFormat(attr.format); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java b/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java deleted file mode 100644 index 102b107e0..000000000 --- a/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.androidide.projects; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.io.File; -import java.io.FileFilter; -import java.util.Collection; -import java.util.HashSet; -import java.util.Locale; - -public class ProjectResourceTable implements IResourceTable { - - private static final ILogger LOG = ILogger.newInstance("ProjectResourceFinder"); - private File resDir; - private File color; - private File layout; - private File[] drawables; - private File[] mipmaps; - - @Override - public File findDrawable(@NonNull String name) { - for (File drawable : drawables) { - final File file = findFileWithName(drawable.listFiles(), name); - if (file != null) { - return file; - } - } - - return null; - } - - @Override - public File findLayout(@NonNull String name) { - return findFileWithName(this.layout.listFiles(), name); - } - - @Override - public String findString(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findString(name); - if (resource == null) { - return name; - } - - final var value = resource.getValue(); - if (value.startsWith("@string/")) { - var ref = value.substring("@string/".length()); - if (name.equals(ref)) { - return name; - } - } - - return value; - } - - @Override - public String findColor(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findColor(name); - if (resource == null) { - return name; - } - - final var value = resource.getValue(); - if (value.startsWith("@color/")) { - var ref = value.substring("@color/".length()); - if (name.equals(ref)) { - return name; - } - } - - return value; - } - - @Override - public String[] findArray(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - if (table == null) { - return new String[] {name}; - } - - final var resource = table.findArray(name); - if (resource == null) { - return new String[] {name}; - } - - return resource.getValues(); - } - - @Override - public String findDimension(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findDimension(name); - if (resource == null) { - return name; - } - - final var value = resource.getValue(); - if (value.startsWith("@dimen/")) { - var ref = value.substring("@dimen/".length()); - if (name.equals(ref)) { - return name; - } - } - - return value; - } - - @Override - public String findBoolean(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findBoolean(name); - if (resource == null) { - return name; - } - - final var val = resource.getValue(); - if (val.startsWith("@bool/")) { - final var ref = val.substring("@bool/".length()); - if (ref.equals(name)) { - // recursive reference - return name; - } - } - - return val; - } - - @Override - public String findInteger(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findInteger(name); - if (resource == null) { - return name; - } - - final var val = resource.getValue(); - if (val.startsWith("@integer/")) { - final var ref = val.substring("@integer/".length()); - if (ref.equals(name)) { - // recursive reference - return name; - } - } - - return val; - } - - @Override - public void setInflatingFile(@NonNull File file) { - if (file.getParentFile() == null) { - throw new IllegalArgumentException("Invalid inflating file"); - } - - setupDirectories( - file // layout file - .getParentFile() // layout dir - .getParentFile()); // res dir - } - - @NonNull - @Override - public Collection listResourceNames(@Nullable String type) { - if (type == null) { - return listAllResourceNames(); - } else { - final var set = new HashSet(); - switch (type) { - case "drawable": - for (var folder : this.drawables) { - listAndAddTo(set, folder, type); - } - break; - case "layout": - listAndAddTo(set, this.layout, type); - break; - case "color": - listAndAddTo(set, this.color, type); - break; - case "mipmap": - for (var folder : this.mipmaps) { - listAndAddTo(set, folder, type); - } - break; - } - - return set; - } - } - - private void setupDirectories(File resDir) { - - if (resDir == null) { - LOG.error("Null resource directory passed to resource finder. Ignoring."); - return; - } - - this.resDir = resDir; - this.color = new File(resDir, "color"); - this.layout = new File(resDir, "layout"); - - final File[] drawables = resDir.listFiles(new NameStartsWith("drawable")); - this.drawables = drawables == null ? new File[0] : drawables; - - final File[] mipmaps = resDir.listFiles(new NameStartsWith("mipmap")); - this.mipmaps = mipmaps == null ? new File[0] : mipmaps; - } - - private File findFileWithName(File[] files, String name) { - if (files == null || name == null) { - return null; - } - - for (File file : files) { - var simpleName = file.getName(); - - if (simpleName.startsWith(name)) { - return file; - } - } - - return null; - } - - private void listAndAddTo(HashSet set, @NonNull File dir, String type) { - final var files = dir.listFiles(); - if (files == null) { - return; - } - - for (var file : files) { - var name = file.getName(); - if (!name.contains(".")) { - continue; - } - - name = name.substring(0, name.lastIndexOf('.')); - name = String.format(Locale.ROOT, "@%s/%s", type, name); - set.add(name); - } - } - - @NonNull - private Collection listAllResourceNames() { - final var set = new HashSet<>(listResourceNames("color")); - set.addAll(listResourceNames("drawable")); - set.addAll(listResourceNames("mipmap")); - set.addAll(listResourceNames("layout")); - return set; - } - - private static class NameStartsWith implements FileFilter { - - private final String name; - - public NameStartsWith(String name) { - this.name = name; - } - - @Override - public boolean accept(@NonNull File file) { - return file.getName().startsWith(name); - } - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragData.java b/app/src/main/java/com/itsaky/androidide/ui/WidgetDragData.java deleted file mode 100755 index f61489793..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragData.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.ui; - -import com.itsaky.androidide.models.UIWidget; -import com.itsaky.inflater.IView; - -/** - * Store data about a draggable widget. Used in UI Designer. - * - * @author Akash Yadav - */ -public class WidgetDragData { - - /** - * Set this to true if the view that is being dragged was already inflated in the layout. If this - * is set to {@code true}, {@link #alreadyInflatedView} must NOT be {@code null}. - * - *

If this is {@code false}, then {@link #newDragData} must be not be null. - */ - public boolean isAlreadyInflated; - - /** - * The view that was already inflated and is being relocated. This must not be null if {@link - * #isAlreadyInflated} is {@code true}. - */ - public IView alreadyInflatedView; - - /** The UIWidget that is being dragged. */ - public UIWidget newDragData; - - private WidgetDragData() {} - - public WidgetDragData( - boolean isAlreadyInflated, IView alreadyInflatedView, UIWidget newDragData) { - this.isAlreadyInflated = isAlreadyInflated; - this.alreadyInflatedView = alreadyInflatedView; - this.newDragData = newDragData; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragListener.java b/app/src/main/java/com/itsaky/androidide/ui/WidgetDragListener.java deleted file mode 100755 index 606b63e47..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragListener.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.ui; - -import android.content.Context; -import android.graphics.RectF; -import android.view.DragEvent; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.SizeUtils; -import com.itsaky.androidide.DesignerActivity; -import com.itsaky.androidide.resources.R; -import com.itsaky.androidide.models.UIWidget; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IView; -import com.itsaky.inflater.IViewGroup; -import com.itsaky.inflater.impl.ErrorUiView; -import com.itsaky.inflater.impl.UiAttribute; -import com.itsaky.inflater.impl.UiView; -import com.itsaky.inflater.impl.UiViewGroup; -import com.itsaky.xml.INamespace; - -import org.jetbrains.annotations.Contract; - -/** - * Common drag listener for handling drag events in all ViewGroups in the UI Designer. - * - * @author Akash Yadav - */ -public class WidgetDragListener implements View.OnDragListener { - - public static final String ANDROID_NS = "android"; - private static final ILogger LOG = ILogger.newInstance("WidgetDragListener"); - private final IView placeholder; - private final IViewGroup viewGroup; - private final OnViewAddedListener addedListener; - private final int PLACEHOLDER_HEIGHT = 20; // in dp - private final int PLACEHOLDER_WIDTH = 40; // in dp - - public WidgetDragListener( - @NonNull Context context, IViewGroup viewGroup, OnViewAddedListener addedListener) { - this.placeholder = new UiView("android.view.View", new View(context), true); - this.viewGroup = viewGroup; - this.addedListener = addedListener; - this.placeholder.asView().setBackgroundResource(R.drawable.bg_widget_drag_placeholder); - this.placeholder - .asView() - .setLayoutParams( - new ViewGroup.LayoutParams( - SizeUtils.dp2px(PLACEHOLDER_WIDTH), SizeUtils.dp2px(PLACEHOLDER_HEIGHT))); - } - - @Override - public boolean onDrag(View v, @NonNull DragEvent event) { - - if (this.viewGroup instanceof ErrorUiView) { - return false; - } - - final var action = event.getAction(); - final var clipDesc = event.getClipDescription(); - final var dragData = (WidgetDragData) event.getLocalState(); - - var handled = true; - switch (action) { - case DragEvent.ACTION_DRAG_STARTED: - handled = clipDesc.hasMimeType(DesignerActivity.DRAGGING_WIDGET_MIME); - break; - case DragEvent.ACTION_DRAG_ENTERED: - case DragEvent.ACTION_DRAG_LOCATION: - this.placeholder.removeFromParent(); // If placeholder is added in another group, remove it. - this.viewGroup.addView(this.placeholder, findPlaceHolderIndex(event.getX(), event.getY())); - break; - case DragEvent.ACTION_DRAG_EXITED: - this.placeholder.removeFromParent(); - break; - case DragEvent.ACTION_DROP: - final var index = this.viewGroup.indexOfChild(this.placeholder); - this.placeholder.removeFromParent(); - - var view = - dragData.isAlreadyInflated - ? dragData.alreadyInflatedView - : createView(dragData.newDragData); - if (!dragData.isAlreadyInflated) { - applyBasicAttributes(view); - } - - // Remove from parent if it has a parent - view.removeFromParent(); - this.viewGroup.addView(view, index); - - if (this.addedListener != null) { - this.addedListener.onViewAdded(view); - } - - break; - } - - return handled; - } - - private void applyBasicAttributes(@NonNull IView view) { - view.addAttribute(layoutWidthAttr()); - view.addAttribute(layoutHeightAttr()); - } - - @NonNull - @Contract(" -> new") - private IAttribute layoutHeightAttr() { - return new UiAttribute(INamespace.ANDROID, "layout_height", "wrap_content"); - } - - @NonNull - @Contract(" -> new") - private IAttribute layoutWidthAttr() { - return new UiAttribute(INamespace.ANDROID, "layout_width", "wrap_content"); - } - - /** - * Finds the index at which the placeholder will be added in the current view group. - * - * @param x The x coordinate of the touch event - * @param y The y coordinate of the touch event - * @return The index at which the placeholder of the view should be added. The returned index is - * always valid index in the {@link #viewGroup}. - */ - private int findPlaceHolderIndex(final float x, final float y) { - final var count = this.viewGroup.getChildCount(); - for (int i = 0; i < this.viewGroup.getChildCount(); i++) { - final var child = this.viewGroup.getChildAt(i); - final var rect = getViewRect(child); - if (rect.contains(x, y)) { - final var top = topHalf(rect); - final var bottom = bottomHalf(rect); - if (top.contains(x, y)) { - return Math.max(0, i - 1); - } else if (bottom.contains(x, y)) { - return Math.min(count, i + 1); - } - } - } - - // If we don't find a suitable index, return the last index - return count; - } - - @NonNull - private RectF getViewRect(@NonNull IView view) { - final var v = view.asView(); - - final var rect = new RectF(); - rect.left = v.getLeft(); - rect.top = v.getTop(); - rect.right = rect.left + v.getWidth(); - rect.bottom = rect.top + v.getHeight(); - - return rect; - } - - private RectF topHalf(RectF src) { - final RectF result = new RectF(src); - result.bottom -= result.height() / 2; - return src; - } - - private RectF bottomHalf(RectF src) { - final RectF result = new RectF(src); - result.top += result.height() / 2; - return src; - } - - @NonNull - private IView createView(@NonNull UIWidget widget) { - try { - final var clazz = widget.asClass(); - final var constructor = clazz.getConstructor(Context.class); - constructor.setAccessible(true); - final var instance = constructor.newInstance(this.viewGroup.asView().getContext()); - - if (instance instanceof ViewGroup) { - return new UiViewGroup(clazz.getName(), (ViewGroup) instance); - } else { - return new UiView(clazz.getName(), instance); - } - } catch (Throwable e) { - LOG.error("Unable to add widget", e); - throw new RuntimeException("Unable to add widget", e); - } - } - - private RectF leftHalf(RectF src) { - final RectF result = new RectF(src); - result.right -= result.width() / 2; - return src; - } - - private RectF rightHalf(RectF src) { - final RectF result = new RectF(src); - result.left += result.width() / 2; - return src; - } - - public static interface OnViewAddedListener { - void onViewAdded(IView view); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragShadowBuilder.java b/app/src/main/java/com/itsaky/androidide/ui/WidgetDragShadowBuilder.java deleted file mode 100755 index cc87c0d85..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/WidgetDragShadowBuilder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.ui; - -import android.graphics.Point; -import android.view.View; - -public class WidgetDragShadowBuilder extends View.DragShadowBuilder { - - private final double TOUCH_Y_OFFSET = 0.3; - - public WidgetDragShadowBuilder(View view) { - super(view); - } - - @Override - public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { - if (getView() != null) { - final var width = getView().getWidth(); - final var height = getView().getHeight(); - outShadowSize.set(width, height); - outShadowTouchPoint.set(width / 2, height + (int) (height * TOUCH_Y_OFFSET)); - } else { - super.onProvideShadowMetrics(outShadowSize, outShadowTouchPoint); - } - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java b/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java deleted file mode 100644 index d45b6f1f8..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.ui; - -import android.annotation.SuppressLint; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.inflater.IView; - -/** - * A touch listener for handling touch events of a view in the UI Designer. - * - *

Some of the views have no effect of setting a click listener or a long click listener (e.g. - * SeekBar). In order to fix this, we use {@link android.view.GestureDetector} and manually handle - * click events. - * - * @author Akash Yadav - */ -public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener - implements View.OnTouchListener { - - private final GestureDetector mGestureDetector; - private final OnClickListener clickListener; - private final OnLongClickListener longClickListener; - - private final IView mView; - - public WidgetTouchListener( - @NonNull IView view, - @Nullable OnClickListener clickListener, - @Nullable OnLongClickListener longClickListener) { - this.mView = view; - this.mGestureDetector = new GestureDetector(view.asView().getContext(), this); - this.clickListener = clickListener; - this.longClickListener = longClickListener; - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View view, @NonNull MotionEvent motionEvent) { - return mGestureDetector.onTouchEvent(motionEvent); - } - - // TODO Should we use onSingleTapConfirmed instead? - @Override - public boolean onSingleTapUp(MotionEvent e) { - if (this.clickListener != null) { - this.clickListener.onClick(this.mView); - } - - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - - if (this.mView.getParent() == null) { - // This is the root layout so this should not be draggable - return; - } - - if (this.longClickListener != null && this.longClickListener.onLongClick(this.mView)) { - this.mView.asView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } - } - - @Override - public boolean onDown(MotionEvent e) { - return true; // We would like to get notified about further events. - } - - /** - * A listener which is invoked when we detect a single click event. - * - * @author Akash Yadav - */ - public interface OnClickListener { - void onClick(IView view); - } - - /** - * A listener which is invoked when we detect a long click event. - * - * @author Akash Yadav - */ - public interface OnLongClickListener { - boolean onLongClick(IView view); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/edit/BaseSuggestionProvider.java b/app/src/main/java/com/itsaky/androidide/ui/edit/BaseSuggestionProvider.java deleted file mode 100644 index 313074821..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/edit/BaseSuggestionProvider.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.ui.edit; - -import com.itsaky.androidide.app.IDEApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.values.ValuesTable; -import com.itsaky.inflater.values.ValuesTableFactory; - -import java.io.File; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * Base class for attribute edit handlers. - * - * @author Akash Yadav - */ -public abstract class BaseSuggestionProvider implements IValueSuggestionProvider { - - protected final File resDir; - protected final File layout; - - protected BaseSuggestionProvider(File layout) { - Objects.requireNonNull(layout); - - final var path = layout.getAbsolutePath(); - if (!Pattern.compile(".*/src/.*/res/layout/(\\w|_)+\\.xml").matcher(path).matches()) { - throw new IllegalArgumentException("Given layout file is not valid"); - } - - this.layout = layout; - this.resDir = Objects.requireNonNull(layout.getParentFile()).getParentFile(); - Objects.requireNonNull(this.resDir); - } - - protected ValuesTable getValuesTable() { - return ValuesTableFactory.getTable(this.resDir); - } - - protected IResourceTable getResourceTable() { - return IDEApplication.getInstance().getResourceTable(); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/edit/DimensionSuggestionProvider.java b/app/src/main/java/com/itsaky/androidide/ui/edit/DimensionSuggestionProvider.java deleted file mode 100644 index d8ce3a3db..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/edit/DimensionSuggestionProvider.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.androidide.ui.edit; - -import androidx.annotation.NonNull; - -import com.itsaky.attrinfo.models.Attr; -import com.itsaky.inflater.IAttribute; -import com.itsaky.androidide.lsp.util.StringUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * Handles editing dimension attributes. - * - * @author Akash Yadav - */ -public class DimensionSuggestionProvider extends BaseSuggestionProvider { - - /** The dimension units that will be suggested. */ - private final List dimensions = new ArrayList<>(); - - public DimensionSuggestionProvider(File layout) { - super(layout); - - this.dimensions.add("dp"); - this.dimensions.add("sp"); - this.dimensions.add("px"); - this.dimensions.add("pt"); - this.dimensions.add("in"); - this.dimensions.add("mm"); - } - - @Override - public boolean checkFormat(int format) { - return (format & Attr.DIMENSION) != 0; - } - - @Override - public @NonNull List suggest(IAttribute attribute, @NonNull String prefix) { - final var list = new ArrayList(); - if (prefix.length() > 0 && Character.isDigit(prefix.charAt(0))) { - // Fixed dimension values - // e.g. 10dp - - var val = extractDimensionVal(prefix); - for (var unit : this.dimensions) { - final var dimension = val + unit; - if (StringUtils.matchesFuzzy(dimension, prefix, true)) { - list.add(dimension); - } - } - } - - return list; - } - - private int extractDimensionVal(@NonNull String prefix) { - StringBuilder s = new StringBuilder(); - var i = 0; - while (i < prefix.length()) { - if (Character.isDigit(prefix.charAt(i))) { - s.append(prefix.charAt(i)); - } else { - break; - } - i++; - } - - return Integer.parseInt(s.toString()); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/ui/edit/IValueSuggestionProvider.java b/app/src/main/java/com/itsaky/androidide/ui/edit/IValueSuggestionProvider.java deleted file mode 100644 index 665f47207..000000000 --- a/app/src/main/java/com/itsaky/androidide/ui/edit/IValueSuggestionProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.ui.edit; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; - -import java.util.List; - -/** - * An attribute value provider provides suggestions while editing an attribute value. - * - * @author Akash Yadav - */ -public interface IValueSuggestionProvider { - - /** - * Check if the given format is supported by this provider. - * - * @param format The formats to check. - * @return true if the given formats contain a supported format, false - * otherwise. - */ - boolean checkFormat(int format); - - /** - * Provide suggestions for the given attribute. - * - * @param attribute The attribute to provide suggestions for. - * @param prefix The prefix (partial identifier) to filter the suggestions. - * @return The suggestion items. Must not be null. - */ - @NonNull - List suggest(IAttribute attribute, String prefix); -} diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java index da59527e7..7d3011451 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java @@ -24,9 +24,9 @@ import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.FLA import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.FLAG_WS_TRAILING; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.FONT_LIGATURES; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.FONT_SIZE; +import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.USE_CUSTOM_FONT; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.USE_ICU; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.USE_MAGNIFER; -import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.USE_CUSTOM_FONT; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.WORD_WRAP; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.getDrawEmptyLineWs; import static com.itsaky.androidide.preferences.internal.EditorPreferencesKt.getDrawInnerWs; @@ -70,7 +70,6 @@ import com.itsaky.androidide.utils.FileUtil; import com.itsaky.androidide.utils.ILogger; import com.itsaky.androidide.utils.LSPUtils; import com.itsaky.androidide.utils.TypefaceUtilsKt; -import com.itsaky.inflater.values.ValuesTableFactory; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -78,7 +77,6 @@ import org.greenrobot.eventbus.ThreadMode; import java.io.File; import java.io.IOException; -import java.util.Objects; import java.util.concurrent.CompletableFuture; import io.github.rosemoe.sora.lang.EmptyLanguage; @@ -385,9 +383,6 @@ public class CodeEditorView extends LinearLayout { private void notifySaved() { binding.editor.dispatchDocumentSaveEvent(); - if (Objects.requireNonNull(getFile()).getName().endsWith(".xml")) { - ValuesTableFactory.syncWithFile(getFile()); - } } public LayoutCodeEditorBinding getBinding() { @@ -422,7 +417,7 @@ public class CodeEditorView extends LinearLayout { LOG.warn("Editor layout is null content=" + binding + ", searchLayout=" + searchLayout); return; } - + searchLayout.beginSearchMode(); } diff --git a/app/src/main/res/layout/activity_designer.xml b/app/src/main/res/layout/activity_designer.xml deleted file mode 100755 index c0dd43e52..000000000 --- a/app/src/main/res/layout/activity_designer.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build-tools/aaptcompiler/src/test/java/com/itsaky/androidide/aaptcompiler/CompilerTest.kt b/build-tools/aaptcompiler/src/test/java/com/itsaky/androidide/aaptcompiler/CompilerTest.kt index 1b2cb34e6..e73862538 100644 --- a/build-tools/aaptcompiler/src/test/java/com/itsaky/androidide/aaptcompiler/CompilerTest.kt +++ b/build-tools/aaptcompiler/src/test/java/com/itsaky/androidide/aaptcompiler/CompilerTest.kt @@ -19,14 +19,17 @@ package com.itsaky.androidide.aaptcompiler import com.android.aaptcompiler.BlameLogger import com.android.aaptcompiler.ResourceCompilerOptions +import com.android.aaptcompiler.ResourceFile +import com.android.aaptcompiler.ResourceName +import com.android.aaptcompiler.XmlProcessor import com.android.aaptcompiler.compileResource +import com.android.aaptcompiler.extractPathData import com.android.utils.StdLogger import com.android.utils.StdLogger.Level.VERBOSE import java.nio.file.Paths import kotlin.io.path.absolute import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 import org.robolectric.RobolectricTestRunner /** @author Akash Yadav */ @@ -45,4 +48,19 @@ class CompilerTest { compileResource(input, output, ResourceCompilerOptions(), BlameLogger(StdLogger(VERBOSE))) println("Compiled in ${System.nanoTime() - start} nano seconds") } + + @Test + fun `test xml processor`() { + val input = + Paths.get(".", "src/test/resources/layout/activity_editor.xml").absolute().normalize().toFile() + val pathData = extractPathData(input, input.absolutePath) + val resFile = ResourceFile( + ResourceName("", pathData.type!!, pathData.name), + pathData.config, + pathData.source, + ResourceFile.Type.ProtoXml + ) + val processor = XmlProcessor(pathData.source, BlameLogger(StdLogger(VERBOSE))) + processor.process(resFile, input.inputStream().buffered()) + } } diff --git a/build-tools/aaptcompiler/src/test/resources/layout/activity_editor.xml b/build-tools/aaptcompiler/src/test/resources/layout/activity_editor.xml new file mode 100755 index 000000000..a62fa02ea --- /dev/null +++ b/build-tools/aaptcompiler/src/test/resources/layout/activity_editor.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/DimensionResource.java b/common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt similarity index 63% rename from xml-inflater/src/main/java/com/itsaky/inflater/values/models/DimensionResource.java rename to common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt index 43c46148e..31b97a3e6 100644 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/DimensionResource.java +++ b/common/src/main/java/com/itsaky/androidide/utils/ResourceUtils.kt @@ -14,18 +14,21 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ -package com.itsaky.inflater.values.models; -import androidx.annotation.NonNull; +package com.itsaky.androidide.utils -/** - * Represents a dimension value. - * - * @author Akash Yadav - */ -public class DimensionResource extends AbstractResource { +import android.content.Context +import android.content.res.Resources.Theme +import android.util.TypedValue - public DimensionResource(@NonNull String name, @NonNull String value) { - super(name, value); - } +@JvmOverloads +fun Context.resolveAttr(id: Int, resolveRefs: Boolean = true): Int { + return theme.resolveAttr(id) } + +@JvmOverloads +fun Theme.resolveAttr(id: Int, resolveRefs: Boolean = true): Int = + TypedValue().let { + resolveAttribute(id, it, resolveRefs) + it.data + } diff --git a/lsp/api/build.gradle b/lsp/api/build.gradle index 303399825..f6280e001 100644 --- a/lsp/api/build.gradle +++ b/lsp/api/build.gradle @@ -69,7 +69,7 @@ dependencies { api project (':lsp:models') api project (':preferences') - compileOnly project (':xml-inflater') + compileOnly project (':sdk-info') compileOnly project (':actions') compileOnly project (':common') } diff --git a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java index c15f81e55..d0c12622d 100644 --- a/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java +++ b/lsp/xml/src/main/java/com/itsaky/androidide/lsp/xml/XMLLanguageServer.java @@ -42,7 +42,6 @@ import com.itsaky.androidide.lsp.xml.providers.CodeFormatProvider; import com.itsaky.androidide.lsp.xml.providers.XmlCompletionProvider; import com.itsaky.androidide.models.Range; import com.itsaky.androidide.projects.api.Project; -import com.itsaky.sdk.SDKInfo; import org.jetbrains.annotations.Nullable; @@ -59,12 +58,11 @@ public class XMLLanguageServer implements ILanguageServer { public static final String SERVER_ID = "xml"; @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - private ILanguageClient client; + private IServerSettings settings; public XMLLanguageServer() {} - @Override public String getServerId() { diff --git a/sdk-info/src/main/java/com/itsaky/apiinfo/ApiInfo.java b/sdk-info/src/main/java/com/itsaky/apiinfo/ApiInfo.java deleted file mode 100755 index 55417ca38..000000000 --- a/sdk-info/src/main/java/com/itsaky/apiinfo/ApiInfo.java +++ /dev/null @@ -1,186 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.apiinfo; - -import android.content.res.Resources; - -import androidx.annotation.NonNull; - -import com.itsaky.apiinfo.models.ClassInfo; -import com.itsaky.apiinfo.models.FieldInfo; -import com.itsaky.apiinfo.models.MethodInfo; -import com.itsaky.androidide.resources.R; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; - -public class ApiInfo { - - public static final String NAME = "name"; - public static final String SINCE = "since"; - public static final String DEPRECATED = "deprecated"; - public static final String REMOVED = "removed"; - public static final String EXTENDS = "extends"; - public static final String IMPLEMENTS = "implements"; - public static final String FIELD = "field"; - public static final String METHOD = "method"; - private final Map classInfos; - private boolean read = false; - - public ApiInfo(@NonNull final Resources resources) throws Exception { - this.classInfos = new HashMap<>(); - doRead(resources); - } - - public void doRead(Resources resources) throws Exception { - final InputStream in = resources.openRawResource(R.raw.versions_xml); - if (in == null) { - throw new Resources.NotFoundException("Cannot find versions.xml"); - } - Document doc = Jsoup.parse(readStream(in)); - Elements classes = doc.getElementsByTag("api").first().getElementsByTag("class"); - if (classes.size() <= 0) { - return; - } - - for (Element clazz : classes) { - ClassInfo info = parseClassInfo(clazz); - if (info != null) classInfos.put(info.name, info); - } - - read = true; - } - - private String readStream(InputStream in) throws IOException { - final StringBuilder sb = new StringBuilder(); - final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line.trim()); - sb.append("\n"); - } - return sb.substring(sb.indexOf(" 0) { - for (Element iface : interfaces) { - info.interfaces.add(iface.attr(NAME)); - } - } - - Elements fields = clazz.getElementsByTag(FIELD); - if (fields.size() > 0) { - for (Element field : fields) { - putField(field, info); - } - } - - Elements methods = clazz.getElementsByTag(METHOD); - if (methods.size() > 0) { - for (Element method : methods) { - putMethod(method, info); - } - } - - return info; - } - - private void putField(Element field, ClassInfo clazz) { - if (!field.hasAttr(NAME)) return; - FieldInfo info = new FieldInfo(); - - info.name = field.attr(NAME); - - if (field.hasAttr(SINCE)) { - info.since = Integer.parseInt(field.attr(SINCE)); - } - - if (field.hasAttr(DEPRECATED)) { - info.deprecated = Integer.parseInt(field.attr(DEPRECATED)); - } - - if (field.hasAttr(REMOVED)) { - info.removed = Integer.parseInt(field.attr(REMOVED)); - } - - clazz.fields.put(info.name, info); - } - - private void putMethod(Element method, ClassInfo clazz) { - if (!method.hasAttr(NAME)) return; - MethodInfo info = new MethodInfo(); - - info.name = method.attr(NAME); - info.simpleName = info.name.substring(0, info.name.indexOf("(")); - - if (method.hasAttr(SINCE)) { - info.since = Integer.parseInt(method.attr(SINCE)); - } - - if (method.hasAttr(DEPRECATED)) { - info.deprecated = Integer.parseInt(method.attr(DEPRECATED)); - } - - if (method.hasAttr(REMOVED)) { - info.removed = Integer.parseInt(method.attr(REMOVED)); - } - - clazz.methods.put(info.name, info); - } - - public boolean hasRead() { - return read; - } - - public ClassInfo getClassByName(String qualifiedName) { - return classInfos.get(qualifiedName); - } -} diff --git a/sdk-info/src/main/java/com/itsaky/apiinfo/models/ClassInfo.java b/sdk-info/src/main/java/com/itsaky/apiinfo/models/ClassInfo.java deleted file mode 100755 index a5d2370d5..000000000 --- a/sdk-info/src/main/java/com/itsaky/apiinfo/models/ClassInfo.java +++ /dev/null @@ -1,88 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.apiinfo.models; - -import android.text.TextUtils; - -import org.eclipse.jdt.core.Signature; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** Holds information about a class */ -public class ClassInfo extends Info { - - /** Interfaces that this class implements */ - public final List interfaces = new ArrayList<>(); - /** Fields of this class */ - public final Map fields = new HashMap<>(); - /** Methods of this class */ - public final Map methods = new HashMap<>(); - /** Superclass of this class */ - public String superClass; - - /** - * Get the field by its simple name - * - * @param name Name of the field to find - */ - public FieldInfo getFieldByName(String name) { - return fields.get(name); - } - - /** - * Get method by its name and parameter types - * - * @param name Name of the method to find - * @param parameters Parameter types of the method - */ - public MethodInfo getMethod(String name, String[] parameters) { - - // Convert parameter types to their binary version - final String[] parameterTypes = new String[parameters.length]; - for (int i = 0; i < parameters.length; i++) { - String paramType = parameters[i]; - String binaryParamType = Signature.createTypeSignature(paramType, true /* isResolved */); - parameterTypes[i] = binaryParamType; - } - - for (Map.Entry entry : methods.entrySet()) { - MethodInfo method = entry.getValue(); - - // Check if this method's simple name is what we are looking for - if (method != null && method.simpleName.trim().equals(name.trim())) { - - // Get parameter types of this method - // Takes binary representation, returns binary representation - String[] paramTypes = Signature.getParameterTypes(method.name); - - // Join the parameters to check if they are equal This is just more efficient - final String params1 = TextUtils.join(",", paramTypes); - final String params2 = TextUtils.join(",", parameterTypes); - - // We found what we were looking for! - if (params1.equals(params2)) return method; - } - } - return null; - } -} diff --git a/sdk-info/src/main/java/com/itsaky/apiinfo/models/FieldInfo.java b/sdk-info/src/main/java/com/itsaky/apiinfo/models/FieldInfo.java deleted file mode 100755 index 650e98416..000000000 --- a/sdk-info/src/main/java/com/itsaky/apiinfo/models/FieldInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.apiinfo.models; - -/** - * Holds information about a field - * - *

No difference as compared to Info - */ -public class FieldInfo extends Info {} diff --git a/sdk-info/src/main/java/com/itsaky/apiinfo/models/Info.java b/sdk-info/src/main/java/com/itsaky/apiinfo/models/Info.java deleted file mode 100755 index bb1e89e9a..000000000 --- a/sdk-info/src/main/java/com/itsaky/apiinfo/models/Info.java +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.apiinfo.models; - -/** Base class for information about a class, method, field */ -public abstract class Info { - - /** Name of this class, method or field */ - public String name = ""; - - /** API level in which this info was deprecated */ - public int deprecated = -1; - - /** API level in which this was added */ - public int since = -1; - - /** API level in which this was removed */ - public int removed = -1; -} diff --git a/sdk-info/src/main/java/com/itsaky/apiinfo/models/MethodInfo.java b/sdk-info/src/main/java/com/itsaky/apiinfo/models/MethodInfo.java deleted file mode 100755 index 68a2d4508..000000000 --- a/sdk-info/src/main/java/com/itsaky/apiinfo/models/MethodInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.apiinfo.models; - -import java.util.ArrayList; -import java.util.List; - -/** Holds information about a method */ -public class MethodInfo extends Info { - - /** Parameter types of this method */ - public final List parameters = new ArrayList<>(); - /** - * In case of a method, the {@link com.itsaky.apiinfo.models.Info#name} contains signature of the - * method This field contains the actual simple name - */ - public String simpleName; -} diff --git a/sdk-info/src/main/java/com/itsaky/attrinfo/AttrInfo.java b/sdk-info/src/main/java/com/itsaky/attrinfo/AttrInfo.java deleted file mode 100755 index c2cd2a059..000000000 --- a/sdk-info/src/main/java/com/itsaky/attrinfo/AttrInfo.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.attrinfo; - -import android.content.res.Resources; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.attrinfo.models.Attr; -import com.itsaky.attrinfo.models.Styleable; -import com.itsaky.androidide.resources.R; -import com.itsaky.xml.INamespace; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.parser.Parser; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.TreeMap; - -/** - * A parser for parsing attrs.xml. This parser maps the attributes with the declared - * styleables. - * - * @author Akash Yadav - */ -public class AttrInfo { - - /** Styles mapped by names; */ - public final Map styles = new HashMap<>(); - - public final Map attributes = new TreeMap<>(); - public final Styleable NO_PARENT; - - public AttrInfo(@NonNull final Resources resources) throws Exception { - NO_PARENT = new Styleable(""); - - Objects.requireNonNull(resources, "Cannot read from null resources."); - - final var in = resources.openRawResource(R.raw.attrs_xml); - parseFromStream(in); - } - - @Nullable - public Attr getAttribute(@NonNull final String name) { - var attr = this.attributes.get(name); - if (attr == null) { - final var a = NO_PARENT.attributes.stream().filter(aa -> aa.name.equals(name)).findFirst(); - attr = a.orElse(null); - } - - return attr; - } - - @NonNull - public Map getAttributes() { - return this.attributes; - } - - @Nullable - public Styleable getStyle(@NonNull final String name) { - return this.styles.get(name); - } - - @NonNull - public Map getStyles() { - return styles; - } - - @Override - public int hashCode() { - return Objects.hash(NO_PARENT, styles); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof AttrInfo)) { - return false; - } - AttrInfo that = (AttrInfo) o; - return Objects.equals(NO_PARENT, that.NO_PARENT) && Objects.equals(styles, that.styles); - } - - @NonNull - @Override - public String toString() { - return "AttrInfoCompat{" + "NO_PARENT=" + NO_PARENT + ", styles=" + styles + '}'; - } - - protected void parseFromStream(InputStream in) throws Exception { - Objects.requireNonNull(in, "Cannot read from null input stream"); - - Document doc = Jsoup.parse(in, null, "", Parser.xmlParser()); - final var resources = doc.getElementsByTag("resources").first(); - - parseFromResources(resources); - } - - protected void parseFromResources(Element resources) { - Objects.requireNonNull(resources, "Cannot parse from null resource element"); - - this.styles.clear(); - - for (int i = 0; i < resources.childrenSize(); i++) { - final var child = resources.child(i); - if (child.tagName().equals("declare-styleable")) { - this.styles.put(parseStyleable(child).name, parseStyleable(child)); - } else if (child.tagName().equals("attr")) { - NO_PARENT.attributes.add(parseAttr(child)); - } - } - - this.styles.put(NO_PARENT.name, NO_PARENT); - } - - @NonNull - protected Styleable parseStyleable(@NonNull final Element styleable) { - checkName(styleable); - - final var name = styleable.attr("name"); - final var style = new Styleable(name); - - for (int i = 0; i < styleable.childrenSize(); i++) { - final var attr = styleable.child(i); - if (!attr.tagName().equals("attr")) { - continue; - } - - style.attributes.add(parseAttr(attr)); - } - - return style; - } - - @NonNull - protected Attr parseAttr(@NonNull final Element attr) { - checkName(attr); - - final var attribute = new Attr(attr.attr("name")); - if (attribute.name.contains(":")) { - final var split = attribute.name.split(":", 2); - attribute.namespace = INamespace.ANDROID; - attribute.name = split[1]; - } - - if (attribute.name.equals("gravity")) { - System.out.println("This is it"); - } - - if (attr.hasAttr("format")) { - attribute.format = Attr.formatForName(attr.attr("format")); - if (attribute.hasFormat(Attr.BOOLEAN)) { - attribute.possibleValues.add("true"); - attribute.possibleValues.add("false"); - } - } - - final var enums = attr.getElementsByTag("enum"); - final var flags = attr.getElementsByTag("flag"); - - if (enums.size() > 0) { - for (var enumEntry : enums) { - checkName(enumEntry); - attribute.possibleValues.add(enumEntry.attr("name")); - } - - if (!attribute.hasFormat(Attr.ENUM)) { - attribute.format |= Attr.ENUM; - } - } - - if (flags.size() > 0) { - for (var flagEntry : flags) { - checkName(flagEntry); - attribute.possibleValues.add(flagEntry.attr("name")); - } - - if (!attribute.hasFormat(Attr.FLAG)) { - attribute.format |= Attr.FLAG; - } - } - - if (this.attributes.containsKey(attribute.name)) { - final var present = this.attributes.get(attribute.name); - if (present != null) { - if (present.hasPossibleValues()) { - attribute.possibleValues.addAll(present.possibleValues); - } - - if (attribute.format == 0 && present.format != 0) { - attribute.format = present.format; - } - } - } - - this.attributes.put(attribute.name, attribute); - - return attribute; - } - - private void checkName(@NonNull Element child) { - if (!child.hasAttr("name")) { - throw new IllegalStateException("Element does not have 'name' attribute: " + child); - } - } -} diff --git a/sdk-info/src/main/java/com/itsaky/attrinfo/models/Attr.java b/sdk-info/src/main/java/com/itsaky/attrinfo/models/Attr.java deleted file mode 100755 index 88359c963..000000000 --- a/sdk-info/src/main/java/com/itsaky/attrinfo/models/Attr.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.attrinfo.models; - -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.xml.INamespace; - -import org.jetbrains.annotations.Contract; - -import java.util.ArrayList; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Pattern; - -/** - * Represents an xml attribute which is read from {@code attrs.xml}. - * - * @author Akash Yadav - * @see com.itsaky.attrinfo.AttrInfo - */ -public class Attr implements Parcelable { - - public static final int REFERENCE = 1; - public static final int COLOR = 1 << 1; - public static final int BOOLEAN = 1 << 2; - public static final int DIMENSION = 1 << 3; - public static final int FLOAT = 1 << 4; - public static final int INTEGER = 1 << 5; - public static final int FRACTION = 1 << 6; - public static final int STRING = 1 << 7; - public static final int ENUM = 1 << 8; - public static final int FLAG = 1 << 9; - public static final int UNKNOWN = 1 << 10; - - public static final String FORMAT_REFERENCE = "reference"; - public static final String FORMAT_COLOR = "color"; - public static final String FORMAT_BOOLEAN = "boolean"; - public static final String FORMAT_DIMENSION = "dimension"; - public static final String FORMAT_FLOAT = "float"; - public static final String FORMAT_INTEGER = "integer"; - public static final String FORMAT_FRACTION = "fraction"; - public static final String FORMAT_STRING = "string"; - public static final String FORMAT_ENUM = "enum"; - public static final String FORMAT_FLAG = "flag"; - public static final Creator CREATOR = - new Creator<>() { - @Override - public Attr createFromParcel(Parcel in) { - return new Attr(in); - } - - @Override - public Attr[] newArray(int size) { - return new Attr[size]; - } - }; - private static final ILogger LOG = ILogger.newInstance("AttrInfo::Attr"); - public INamespace namespace; - public String name; - public Set possibleValues; - public int format; - - public Attr(String name) { - this(name, INamespace.ANDROID); - } - - public Attr(String name, INamespace namespace) { - this.name = name; - this.namespace = namespace; - this.possibleValues = new TreeSet<>(); - } - - protected Attr(@NonNull Parcel in) { - this.namespace = in.readParcelable(INamespace.class.getClassLoader()); - this.name = in.readString(); - this.format = in.readInt(); - - final var list = new ArrayList(); - in.readStringList(list); - this.possibleValues = new TreeSet<>(list); - } - - public static int formatForName(@NonNull String names) { - var result = 0; - if (names.contains("|")) { - for (var name : names.split(Pattern.quote("|"))) { - result |= formatForSingleName(name); - } - } else { - result = formatForSingleName(names); - } - return result; - } - - @Contract(pure = true) - public static int formatForSingleName(@NonNull String name) { - switch (name) { - case FORMAT_REFERENCE: - return REFERENCE; - case FORMAT_COLOR: - return COLOR; - case FORMAT_BOOLEAN: - return BOOLEAN; - case FORMAT_DIMENSION: - return DIMENSION; - case FORMAT_FLOAT: - return FLOAT; - case FORMAT_INTEGER: - return INTEGER; - case FORMAT_FRACTION: - return FRACTION; - case FORMAT_STRING: - return STRING; - case FORMAT_ENUM: - return ENUM; - case FORMAT_FLAG: - return FLAG; - } - - return UNKNOWN; - } - - public static String createFormatText(int format) { - final var formats = new ArrayList(); - if ((format & REFERENCE) != 0) { - formats.add(FORMAT_REFERENCE); - } - - if ((format & COLOR) != 0) { - formats.add(FORMAT_COLOR); - } - - if ((format & BOOLEAN) != 0) { - formats.add(FORMAT_BOOLEAN); - } - - if ((format & DIMENSION) != 0) { - formats.add(FORMAT_DIMENSION); - } - - if ((format & FLOAT) != 0) { - formats.add(FORMAT_FLOAT); - } - - if ((format & INTEGER) != 0) { - formats.add(FORMAT_INTEGER); - } - - if ((format & FRACTION) != 0) { - formats.add(FORMAT_FRACTION); - } - - if ((format & STRING) != 0) { - formats.add(FORMAT_STRING); - } - - if ((format & ENUM) != 0) { - formats.add(FORMAT_ENUM); - } - - if ((format & FLAG) != 0) { - formats.add(FORMAT_FLAG); - } - - if (formats.isEmpty()) { - formats.add("unknown"); - } - - return TextUtils.join("|", formats); - } - - public boolean hasPossibleValues() { - return possibleValues != null && possibleValues.size() > 0; - } - - public boolean hasFormat(int format) { - return (this.format & format) != 0; - } - - @Override - public int hashCode() { - return Objects.hash(namespace, name, possibleValues, format); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - Attr attr = (Attr) o; - return format == attr.format - && Objects.equals(namespace, attr.namespace) - && Objects.equals(name, attr.name) - && Objects.equals(possibleValues, attr.possibleValues); - } - - @NonNull - @Override - public String toString() { - return "Attr [" - + " name: " - + name - + "\n" - + " namespace: " - + namespace - + "\n" - + " values: " - + TextUtils.join(", ", this.possibleValues) - + "\n" - + " format: " - + format - + "\n" - + "]"; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(namespace, flags); - dest.writeString(name); - dest.writeInt(format); - dest.writeStringList(new ArrayList<>(this.possibleValues)); - } -} diff --git a/sdk-info/src/main/java/com/itsaky/attrinfo/models/Styleable.java b/sdk-info/src/main/java/com/itsaky/attrinfo/models/Styleable.java deleted file mode 100644 index ebe1063eb..000000000 --- a/sdk-info/src/main/java/com/itsaky/attrinfo/models/Styleable.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.attrinfo.models; - -import androidx.annotation.NonNull; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * A <declare-styleable>. - * - * @author Akash Yadav - */ -public class Styleable { - - public final String name; - public final Set attributes = new HashSet<>(); - - public Styleable(String name) { - if (name == null || name.trim().length() <= 0) { - throw new IllegalArgumentException("Invalid name specified for declared styleable"); - } - - this.name = name.trim(); - } - - @Override - public int hashCode() { - return Objects.hash(name, attributes); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Styleable)) { - return false; - } - Styleable styleable = (Styleable) o; - return Objects.equals(name, styleable.name) && Objects.equals(attributes, styleable.attributes); - } - - @NonNull - @Override - public String toString() { - return "Styleable{" + "name='" + name + '\'' + ", attributes=" + attributes + '}'; - } -} diff --git a/sdk-info/src/main/java/com/itsaky/sdk/SDKInfo.java b/sdk-info/src/main/java/com/itsaky/sdk/SDKInfo.java deleted file mode 100644 index 252e0c6ff..000000000 --- a/sdk-info/src/main/java/com/itsaky/sdk/SDKInfo.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.sdk; - -import android.content.Context; -import android.content.res.Resources; - -import androidx.annotation.NonNull; - -import com.itsaky.apiinfo.ApiInfo; -import com.itsaky.attrinfo.AttrInfo; -import com.itsaky.widgets.WidgetInfo; - -import java.util.Objects; - -/** - * Class that holds all of the 'sdk-info' APIs. - * - * @see ApiInfo - * @see AttrInfo - * @see WidgetInfo - * @author Akash Yadav - */ -public class SDKInfo { - - private final AttrInfo attrInfo; - private final ApiInfo apiInfo; - private final WidgetInfo widgetInfo; - - public SDKInfo(@NonNull final Context context) throws Exception { - this(context.getResources()); - } - - public SDKInfo(@NonNull final Resources resources) throws Exception { - Objects.requireNonNull(resources); - this.apiInfo = new ApiInfo(resources); - this.attrInfo = new AttrInfo(resources); - this.widgetInfo = new WidgetInfo(resources); - } - - public AttrInfo getAttrInfo() { - return attrInfo; - } - - public ApiInfo getApiInfo() { - return apiInfo; - } - - public WidgetInfo getWidgetInfo() { - return widgetInfo; - } -} diff --git a/sdk-info/src/main/java/com/itsaky/widgets/WidgetInfo.java b/sdk-info/src/main/java/com/itsaky/widgets/WidgetInfo.java deleted file mode 100755 index 3cce0cbfd..000000000 --- a/sdk-info/src/main/java/com/itsaky/widgets/WidgetInfo.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.widgets; - -import android.content.res.Resources; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.widgets.models.Widget; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Collection; -import java.util.SortedMap; -import java.util.TreeMap; - -public class WidgetInfo { - - private final SortedMap widgets; - private final SortedMap params; - - public WidgetInfo(@NonNull final Resources resources) throws IOException { - this.widgets = new TreeMap<>(); - this.params = new TreeMap<>(); - readWidgets(resources); - } - - private void readWidgets(@NonNull final Resources resources) throws IOException { - final InputStream in = resources.openRawResource(com.itsaky.androidide.resources.R.raw.widgets); - if (in == null) { - return; - } - - final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String line; - while ((line = reader.readLine()) != null) { - final var split = line.split("\\s"); - final var name = split[0]; - if (name == null || name.trim().isEmpty()) { - continue; - } - - final var code = name.charAt(0); - - final var entryName = name.substring(1); - final var isLayout = code == 'L'; // L -> Layout, W -> Widget, P -> LayoutParam - final var superclasses = new String[split.length - 1]; - - System.arraycopy(split, 1, superclasses, 0, superclasses.length); - - if (code == 'P') { - final var className = entryName.substring(0, entryName.lastIndexOf(".")); - this.params.put(className, superclasses); - } else { - final var widget = new Widget(entryName, isLayout); - widget.superclasses = superclasses; - widgets.put(entryName, widget); - } - } - } - - public Widget getWidget(String name) { - return this.widgets.getOrDefault(name, null); - } - - @Nullable - public Widget getWidgetBySimpleName(@NonNull final String name) { - for (final var entry : this.widgets.entrySet()) { - final var key = entry.getKey(); - final var simple = key.substring(key.lastIndexOf(".") + 1); - if (simple.equals(name)) { - return entry.getValue(); - } - } - return null; - } - - public Collection getWidgets() { - return this.widgets.values(); - } - - public String[] getLayoutParamSuperClasses(final String className) { - return this.params.get(className); - } -} diff --git a/sdk-info/src/main/java/com/itsaky/widgets/models/Widget.java b/sdk-info/src/main/java/com/itsaky/widgets/models/Widget.java deleted file mode 100755 index 075cec0b2..000000000 --- a/sdk-info/src/main/java/com/itsaky/widgets/models/Widget.java +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.widgets.models; - -import androidx.annotation.NonNull; - -public class Widget implements Comparable { - - public String name; - public String simpleName; - public boolean isViewGroup; - - public String[] superclasses = new String[0]; - - public Widget(@NonNull String name, boolean isViewGroup) { - this.name = name; - this.simpleName = name.substring(name.lastIndexOf(".") + 1); - this.isViewGroup = isViewGroup; - } - - @Override - public int compareTo(Widget that) { - if (that != null) { - return this.simpleName.compareTo(that.simpleName); - } - - return -1; - } -} diff --git a/settings.gradle b/settings.gradle index 6ff5dc06e..de5204aef 100755 --- a/settings.gradle +++ b/settings.gradle @@ -12,7 +12,6 @@ include ':lookup' include ':sdk-info' include ':shared' include ':treeview' -include ':xml-inflater' include ':gradle-plugin' diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar index 235ecfe3b..6387e73c5 100644 Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ diff --git a/xml-inflater/.gitignore b/xml-inflater/.gitignore deleted file mode 100644 index bb0d480e4..000000000 --- a/xml-inflater/.gitignore +++ /dev/null @@ -1,85 +0,0 @@ -# Built application files -*.apk -*.aar -*.ap_ -*.aab - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -/bin/ -/gen/ -/out/ -# Uncomment the following line in case you need and you don't have the release build type files in your app -# release/ - -# Gradle files -.gradle/ -/build/ - -# Local configuration file (sdk path, etc) -local.properties - -# Proguard folder generated by Eclipse -proguard/ - -# Log Files -*.log - -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# IntelliJ -*.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -# Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild -.cxx/ - -# Google Services (e.g. APIs or Firebase) -# google-services.json - -# Freeline -freeline.py -freeline/ -freeline_project_description.json - -# fastlane -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output -fastlane/readme.md - -# Version control -vcs.xml - -# lint -lint/intermediates/ -lint/generated/ -lint/outputs/ -lint/tmp/ -# lint/reports/ diff --git a/xml-inflater/build.gradle b/xml-inflater/build.gradle deleted file mode 100644 index 4d2c21612..000000000 --- a/xml-inflater/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - namespace "com.itsaky.inflater" - compileSdkVersion rootProject.ext.compileSdk - buildToolsVersion rootProject.ext.buildTools - - defaultConfig { - minSdkVersion rootProject.ext.minSdk - targetSdkVersion rootProject.ext.targetSdk - } - - compileOptions { - sourceCompatibility rootProject.ext.javaSourceVersion - targetCompatibility rootProject.ext.javaTargetVersion - } - - kotlinOptions { - jvmTarget = "11" - } - - buildTypes { - release { - minifyEnabled true - } - } -} - -dependencies { - implementation fileTree (dir: 'libs', include: ['*.jar']) - implementation androidx.ktx - implementation androidx.annotation - implementation androidx.appcompat - - implementation google.guava - implementation common.utilcode - implementation common.jsoup - implementation common.kotlin - - implementation project (":common") - implementation project (":resources") - api project (':sdk-info') -} \ No newline at end of file diff --git a/xml-inflater/proguard-rules.pro b/xml-inflater/proguard-rules.pro deleted file mode 100644 index 0448f7080..000000000 --- a/xml-inflater/proguard-rules.pro +++ /dev/null @@ -1,17 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /Users/bogdan/Library/Android/sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/xml-inflater/src/main/AndroidManifest.xml b/xml-inflater/src/main/AndroidManifest.xml deleted file mode 100644 index 9c5d2fe0b..000000000 --- a/xml-inflater/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/AttrType.java b/xml-inflater/src/main/java/com/itsaky/inflater/AttrType.java deleted file mode 100644 index f93b9b1f5..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/AttrType.java +++ /dev/null @@ -1,117 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -/** - * Value type of {@link IAttribute} - * - * @author Akash Yadav - */ -public enum AttrType { - - /** Refers to another resource */ - REFERENCE("reference"), - - /** Color value */ - COLOR("color"), - - /** Either {@code true} or {@code false} */ - BOOLEAN("boolean"), - - /** A dimension like 10dp, 14sp etc. */ - DIMENSION("dimension"), - - /** A floating point number. E.g.: 1.9, 123.45 */ - FLOAT("float"), - - /** An integer value */ - INTEGER("integer"), - - /** A string value */ - STRING("string"), - - /** A fraction */ - FRACTION("fraction"), - - /** A enum value. Unlike {@link #FLAG}, an attribute can have only one of this. */ - ENUM("enum"), - - /** A flag value. May contain on or more values */ - FLAG("flag"), - - /** An unknown value. The layout inflater does not consider this */ - UNKNOWN(""); - - public static final String FORMAT_REF = REFERENCE.getFormat(); - public static final String FORMAT_COLOR = COLOR.getFormat(); - public static final String FORMAT_BOOL = BOOLEAN.getFormat(); - public static final String FORMAT_DIMEN = DIMENSION.getFormat(); - public static final String FORMAT_FLOAT = FLOAT.getFormat(); - public static final String FORMAT_INT = INTEGER.getFormat(); - public static final String FORMAT_STRING = STRING.getFormat(); - public static final String FORMAT_FRACTION = FRACTION.getFormat(); - public static final String FORMAT_ENUM = ENUM.getFormat(); - public static final String FORMAT_FLAG = FLAG.getFormat(); - private final String format; - - private AttrType(String format) { - this.format = format; - } - - /** - * Get the attribute type from format - * - * @param format Format, as declared in styleables. - * @return The Attribute type - */ - public static AttrType fromFormat(String format) { - if (FORMAT_REF.equals(format)) { - return AttrType.REFERENCE; - } else if (FORMAT_COLOR.equals(format)) { - return AttrType.COLOR; - } else if (FORMAT_BOOL.equals(format)) { - return AttrType.BOOLEAN; - } else if (FORMAT_DIMEN.equals(format)) { - return AttrType.DIMENSION; - } else if (FORMAT_FLOAT.equals(format)) { - return AttrType.FLOAT; - } else if (FORMAT_INT.equals(format)) { - return AttrType.INTEGER; - } else if (FORMAT_STRING.equals(format)) { - return AttrType.STRING; - } else if (FORMAT_FRACTION.equals(format)) { - return AttrType.FRACTION; - } else if (FORMAT_ENUM.equals(format)) { - return AttrType.ENUM; - } else if (FORMAT_FLAG.equals(format)) { - return AttrType.FLAG; - } - - return AttrType.UNKNOWN; - } - - public String getFormat() { - return this.format; - } - - public boolean hasFixedValues() { - return this == AttrType.ENUM || this == AttrType.FLAG; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/BaseLayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/BaseLayoutInflater.java deleted file mode 100644 index a89beb16f..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/BaseLayoutInflater.java +++ /dev/null @@ -1,73 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import java.util.HashSet; -import java.util.Set; - -public abstract class BaseLayoutInflater extends ILayoutInflater { - - private final Set inflateListeners = new HashSet<>(); - protected boolean notify = true; - - @Override - public void registerInflateListener(IInflateListener listener) { - inflateListeners.add(listener); - } - - @Override - public void unregisterListener(IInflateListener listener) { - inflateListeners.remove(listener); - } - - protected void preInflate() { - if (notify) { - for (IInflateListener listener : inflateListeners) { - listener.onBeginInflate(); - } - } - } - - protected void postApplyAttribute(IAttribute attr, IView view) { - if (notify) { - for (IInflateListener listener : inflateListeners) { - listener.onApplyAttribute(attr, view); - } - } - } - - protected void postCreateView(IView view) { - if (notify) { - for (IInflateListener listener : inflateListeners) { - listener.onInflateView(view, view.getParent()); - } - } - } - - protected void postInflate(IView root) { - if (notify) { - for (IInflateListener listener : inflateListeners) { - listener.onFinishInflate(root); - } - } - } - - protected Set getInflateListeners() { - return this.inflateListeners; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java b/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java deleted file mode 100644 index a2c6fc93f..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IAttribute.java +++ /dev/null @@ -1,110 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.xml.INamespace; - -import java.util.Comparator; - -/** - * Attribute that can be applied to a {@link IView} - * - * @author Akash Yadav - */ -public interface IAttribute { - - /** - * Returned by {@link #compareNull(Object, Object)} to indicate that the comparison must be done - * further. - */ - int DO_COMPARE = 2; - - Comparator COMPARATOR = - (first, second) -> { - var result = compareNull(first, second); - if (result != IAttribute.DO_COMPARE) { - return result; - } - - result = compareNull(first.getNamespace(), second.getNamespace()); - if (result != IAttribute.DO_COMPARE) { - return result; - } - - // If we reach here, then it means that the attributes and their namespaces are not - // null - // First, the attributes must be compared by their namespaces - - final var firstNs = first.getNamespace(); - final var secondNs = second.getNamespace(); - - if (firstNs != null && firstNs.equals(secondNs)) { - // Compare by attribute names if the namespaces are same - return first.getAttributeName().compareTo(second.getAttributeName()); - } - - // xmlns declarations must be on top - if (firstNs != null && firstNs.equals(INamespace.DECLARATOR)) { - return -1; - } - - if (secondNs != null && secondNs.equals(INamespace.DECLARATOR)) { - return 1; - } - - return 0; - }; - - private static int compareNull(Object first, Object second) { - if (first == null && second == null) { - return 0; - } - - if (first != null && second == null) { - return -1; - } - - if (first == null) { - return 1; - } - - return DO_COMPARE; - } - - @Nullable - INamespace getNamespace(); - - @NonNull - String getAttributeName(); - - @NonNull - String getValue(); - - /** - * Set the given string as a value to this attribute. This will set {@link #isApplied()} to return - * {@code true}. - * - * @param value The value to set. - */ - void apply(String value); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IAttributeAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/IAttributeAdapter.java deleted file mode 100644 index a4f41f655..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IAttributeAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import android.view.View; - -/** An adapter that handles attributes of a specific type of view */ -public interface IAttributeAdapter { - - /** - * Set the resource finder used by this class. - * - * @param resourceFinder The resource finder. - */ - void setResourceFinder(IResourceTable resourceFinder); - - /** - * Can this adapter handle attributes of the provided view? - * - * @param view The view to which the attributes will be applied - * @return {@code true} if this adapter can handle attributes of the specified view - */ - boolean isApplicableTo(View view); - - /** - * Apply the provided attribute to the view. - * - *

This will be called if and only if {@link #isApplicableTo(View)} returns {@code true}. - * - * @param attribute The attribute to apply - * @param view The view to apply attribute to - * @return {@code true} if the attribute was applied successfully {@code false} otherwise. - */ - boolean apply(IAttribute attribute, View view); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IDTable.java b/xml-inflater/src/main/java/com/itsaky/inflater/IDTable.java deleted file mode 100644 index e06692f5b..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IDTable.java +++ /dev/null @@ -1,84 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import android.view.View; - -import java.util.HashMap; -import java.util.Map; - -/** - * Holds references to view IDs. The {@link #newRound()} must be called before inflating a layout - * from the XML. This will make sure that we cleanup the existing IDs. - * - * @author Akash Yadav - */ -public class IDTable { - - /** - * Map containing the IDs. Entries are mapped by the name of the ID. - * - *

For example : if android:id="@+id/my_id" is provided for view, then an entry will be created - * in this map with the key : 'my_id' and a random integer as the value. - * - *

No entries are created for views which do not have the android:id attribute. - */ - private static final Map ids = new HashMap<>(); - - /** Clears the existing ID map. */ - public static void newRound() { - ids.clear(); - } - - /** - * Creates a new ID entry for the given name. - * - * @param idName The name of the ID. - * @return The assigned ID. - */ - public static int newId(String idName) { - - { - // In some cases, @+id might be used as a reference - // to another ID. So we check if an ID with the same - // name is already present or not. - final int id = getId(idName); - if (id != View.NO_ID) { - return id; - } - } - - final int id = View.generateViewId(); - ids.put(idName, id); - return id; - } - - /** - * Get the ID mapped by the given name. - * - * @param name The name of the ID. - * @return The mapped ID or {@link android.view.View#NO_ID} - */ - public static int getId(String name) { - final Integer id = ids.getOrDefault(name, null); - if (id == null) { - return View.NO_ID; - } - return id; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IFixedValue.java b/xml-inflater/src/main/java/com/itsaky/inflater/IFixedValue.java deleted file mode 100644 index 660cce54b..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IFixedValue.java +++ /dev/null @@ -1,49 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -/** - * A fixed value which represents an enum of a flag - * - * @author Akash Yadav - */ -public interface IFixedValue { - - /** - * Is this value a flag? An attribute which accepts a flag may be combined with other flags. - * - * @return {@code true} if this value is a flag. {@code false} otherwise. - */ - boolean isFlag(); - - /** - * The name of this enum/flag as represented in an XML file - * - * @return The name of this flag - */ - String getName(); - - /** - * Get the value of this enum/flag. This value is fixed and should not change. - * - * @return Value of this enum/flag. - */ - int getValue(); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IInflateListener.java b/xml-inflater/src/main/java/com/itsaky/inflater/IInflateListener.java deleted file mode 100644 index 8a66b7856..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IInflateListener.java +++ /dev/null @@ -1,52 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -/** - * A listener which is invoked to notify about events in {@link ILayoutInflater} - * - * @author Akash Yadav - */ -public interface IInflateListener { - - /** Called when the inflation process is about to start */ - void onBeginInflate(); - - /** - * Called when a view has been inflated - * - * @param view The view that will be inflated - * @param parent The parent of this view - */ - void onInflateView(IView view, IViewGroup parent); - - /** - * Called when the attribute has been applied to the specified view - * - * @param attr The applied attribute - * @param view The view to which attribute was applied - */ - void onApplyAttribute(IAttribute attr, IView view); - - /** - * Called when the inflation process finished successfully - * - * @param The root rootView of the inflated layout - */ - void onFinishInflate(IView rootView); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java deleted file mode 100644 index 2d377fbc5..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/ILayoutInflater.java +++ /dev/null @@ -1,185 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import android.content.Context; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.blankj.utilcode.util.FileIOUtils; -import com.blankj.utilcode.util.FileUtils; -import com.itsaky.inflater.util.Preconditions; - -import java.io.File; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A layout inflater which inflates layout from a raw XML file - * - * @author Akash Yadav - */ -public abstract class ILayoutInflater { - - private static final Pattern XML_DECL = Pattern.compile("<\\?xml.*\\?>"); - - /** The file whose content is currently being inflated. */ - private File currentlyInflatingFile; - - /** - * Creates an instance of {@code ILayoutInflater} - * - * @param config The configuration of the LayoutInflater - * @return A new layout inflater instance - */ - @NonNull - public static ILayoutInflater newInstance(LayoutInflaterConfiguration config) { - return new XMLLayoutInflater(config); - } - - /** - * Reset the {@link ContextProvider} of this inflater - * - * @param provider The new {@link ContextProvider} - */ - public abstract void resetContextProvider(ContextProvider provider); - - /** - * Register the inflate listener to this inflater. - * - *

NOTE: Do not register too much listeners as they may affect the inflation time. - * - * @param listener The listener to register. - */ - public abstract void registerInflateListener(IInflateListener listener); - - /** - * Unregister the inflate listener if it is registered. - * - * @param listener The listener to unregister - */ - public abstract void unregisterListener(IInflateListener listener); - - /** - * Inflate the layout from the given file path - * - * @param path The file path to inflate layout from - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - public IView inflatePath(String path, ViewGroup parent) throws InflateException { - Preconditions.assertNotBlank(path, "Layout file path is blank!"); - return inflate(new File(path), parent); - } - - /** - * Inflate the layout from the given file - * - * @param file The file to inflate layout from - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - public IView inflate(File file, ViewGroup parent) throws InflateException { - - Preconditions.assertNotnull(file, "Cannot inflate null file"); - - // How can we inflate a non existent file? - if (!file.exists()) { - throw new InflateException("File does not exist!"); - } - - if (!file.canRead()) { - throw new InflateException("Cannot read file. Permission denied!"); - } - - // Cannot inflate a non UTF-8 too. This will also be true if the file is empty. - if (!FileUtils.isUtf8(file)) { - throw new InflateException("File is not UTF-8 or is empty!"); - } - - requireResourceTable().setInflatingFile(file); - this.currentlyInflatingFile = file; - return inflate(FileIOUtils.readFile2String(file), parent); - } - - /** - * Get the resource finder attached to this layout inflater. - * - * @return The attached resource finder. - */ - @NonNull - protected abstract IResourceTable requireResourceTable(); - - /** - * Inflate the layout from the provided XML layout code - * - * @param layout The XML layout code - * @return The inflated layout - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - protected IView inflate(String layout, ViewGroup parent) throws InflateException { - - Preconditions.assertNotBlank(layout, "Layout is blank!"); - - // Trim the layout code - layout = layout.trim(); - - final Matcher matcher = XML_DECL.matcher(layout); - if (matcher.find()) { - layout = layout.substring(matcher.end()).trim(); - } - - return doInflate(layout, parent); - } - - /** - * Acatual implementation of the layout inflation - * - * @param layout The layout to inflate - * @return The inflated view - * @throws InflateException when there was an error inflating the layout - */ - @NonNull - protected abstract IView doInflate(String layout, ViewGroup parent) throws InflateException; - - /** - * Get the currently inflating file. - * - * @return The file. - */ - @Nullable - protected File getFile() { - return currentlyInflatingFile; - } - - /** Provides a reference to {@link Context}. This will be used to create original view. */ - public interface ContextProvider { - - /** - * Provide the context to this inflater. - * - * @return The context - */ - Context getContext(); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IResourceTable.java b/xml-inflater/src/main/java/com/itsaky/inflater/IResourceTable.java deleted file mode 100644 index 04cc64b3f..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IResourceTable.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.io.File; -import java.util.Collection; - -/** Finds values in resources of a project */ -public interface IResourceTable { - - /** - * Find the path of the drawable with the provided name - * - *

This method will be called if the value of attribute starts with '@drawable/' - * - * @param name The name of the drawable - * @return The drawable resource. - */ - File findDrawable(@NonNull String name); - - /** - * Find the layout file with the specified name. - * - * @param name The name of the layout file - * @return The found layout resource file - */ - File findLayout(@NonNull String name); - - /** - * Find a string resource. Must be a valid string or {@code null} - * - * @param name The name of the resource - * @return Value of the resource - */ - String findString(@NonNull String name); - - /** - * Find a color resource. The returned value must be a valid color value or {@code -1} - * - * @param name The name of the resource - * @return Value of the resource - */ - String findColor(@NonNull String name); - - /** - * Find an array resource. Must be a valid array or {@code null} - * - * @param name The name of the resource - * @return Value of the resource - */ - String[] findArray(@NonNull String name); - - /** - * Find the dimension value with the given name. - * - * @param name The name of the dimension resource - * @return The String representation of the dimension. It could one of the following format : 1dp, - * 12sp, 123px, etc. - */ - String findDimension(@NonNull String name); - - /** - * Find the boolean value with the given name. - * - * @param name The name of the boolean resource. - * @return The string value of the boolean resource. - */ - String findBoolean(@NonNull String name); - - /** - * Find the integer value with the given name. - * - * @param name The name of the integer resource. - * @return The string value of the integer resource. - */ - String findInteger(@NonNull String name); - - /** - * Set the file which is currently being inflated. - * - * @param file The file. - */ - void setInflatingFile(@NonNull File file); - - /** - * Lists the resources with the given type. - * - * @param type The type of the resources to list. Might be null. - * @return A collection of resource names. If type is null, all the - * resources in the resource table must be listed. - */ - @NonNull - Collection listResourceNames(@Nullable String type); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IView.java b/xml-inflater/src/main/java/com/itsaky/inflater/IView.java deleted file mode 100644 index e8b837c6c..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IView.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.xml.INamespace; - -import java.util.List; - -/** - * Represents a view in the UI Designer - * - * @author Akash Yadav - */ -public interface IView { - - /** - * The indentation length that will be used while generating layout code. You can modify this to - * change the number of spaces used for indentation. - */ - int DEFAULT_INDENTATION_LENGTH = 4; - - /** - * Get this view as android {@link View}. This will be used to display this view in the designer - * - * @return {@link View} of this object. Maybe {@code null} - */ - View asView(); - - /** - * Add this attribute to this view - * - * @param attr The Attribute to add - */ - void addAttribute(IAttribute attr); - - /** - * Remove this attribute - * - * @param attr The Attribute to remove - */ - void removeAttribute(IAttribute attr); - - /** - * Remove attribute at index - * - * @param index Index of the attribute to remove - */ - void removeAttributeAt(int index); - - /** - * Get the attributes of this view. - * - * @return The attributes. - */ - List getAttributes(); - - /** - * Get the attributes of this view as an array. - * - * @return The attributes. - * @see #getAttributes() - */ - IAttribute[] getAttrArray(); - - /** - * Does this view contains an attribute with the given namespace and name? - * - * @param namespace The namespace to check. - * @param name The name of the attribute to check. - * @return {@code true} if this view contains the attribute, {@code false} otherwise. - */ - boolean hasAttribute(INamespace namespace, String name); - - /** - * Get the attribute with the given namespace and attribute name. Or {@code null} if there is no - * attribute with the given namespace and name.s - * - * @param namespace The namespace of the attribute. - * @param name The name of the attribute. - * @return Found {@link IAttribute} or {@code null}. - */ - @Nullable - IAttribute getAttribute(INamespace namespace, String name); - - /** - * Update the value of the given attribute. - * - * @param attribute The attribute that should be updated. - * @return {@code true} if the attribute was successfully updated, {@code false} otherwise. - */ - default boolean updateAttribute(@NonNull IAttribute attribute) { - return updateAttribute( - attribute.getNamespace(), attribute.getAttributeName(), attribute.getValue()); - } - - /** - * Find and update the given attribute with the given value. - * - * @param namespace The namespace of the attribute. - * @param name The name of the attribute. - * @param value The new value of the attribute. - * @return {@code true} if the attribute was successfully updated, {@code false} otherwise. - */ - boolean updateAttribute(INamespace namespace, String name, String value); - - /** - * Register this attribute adapter - * - * @param adapter The adapter to register - */ - void registerAttributeAdapter(IAttributeAdapter adapter); - - /** - * Register the given namespace to this view. - * - * @param namespace The name space to register. - */ - void registerNamespace(INamespace namespace); - - /** - * Find the namespace registered by the given name. Returns null if there is none. - * - * @param name The name of the namespace to look for. Must not be null. - * @return The found namespace or null. - */ - @Nullable - INamespace findRegisteredNamespace(@NonNull String name); - - /** - * Get the parent of this view - * - * @return The parent of this view or {@code null} if this is the root view. - */ - IViewGroup getParent(); - - /** - * Set the parent of this view. This is set by {@link IViewGroup} when {@link - * IViewGroup#addView(IView, int)} is called. - * - * @param newParent The new parent of this view. - */ - void setParent(IViewGroup newParent); - - /** - * Remove this view from its parent. This should take care of removing the actual {@link View} - * from its parent. - * - * @return {@code true} if the view was successfully removed, {@code false} otherwise. - */ - boolean removeFromParent(); - - /** - * Is this view a placeholder for another view? - * - * @return {@code true} if this view is a placeholder, {@code false} otherwise. - */ - boolean isPlaceholder(); - - /** - * Get the stored data. - * - * @return The stored data. - * @see #setExtraData(Object) - */ - @Nullable - Object getExtraData(); - - /** - * Store an object that is associated with this view. - * - * @param data The data to store. - */ - void setExtraData(Object data); - - /** - * @see #generateCode(int) - */ - default String generateCode() { - return generateCode(0); - } - - /** - * Get the layout resource code for this file. - * - * @param indentationLength The number of spaces to indent. - */ - String generateCode(int indentationLength); - - /** - * Get the tag name which will be used for generating XML Code for this view. - * - *

Default implementation simply returns the simple name of this view's class. - * - * @return The tag name of this view. - */ - String getXmlTag(); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/IViewGroup.java b/xml-inflater/src/main/java/com/itsaky/inflater/IViewGroup.java deleted file mode 100644 index f9146514d..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/IViewGroup.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater; - -import android.view.View; - -import java.util.List; -import java.util.function.Consumer; - -/** - * Represents a {@link android.view.ViewGroup} - * - * @author Akash Yadav - */ -public interface IViewGroup extends IView { - - /** - * Add this view to this group - * - * @param view The view to add - */ - void addView(IView view); - - /** - * Add this view to this group - * - * @param view The view to add - * @param index Add this view to this index - */ - void addView(IView view, int index); - - /** - * Remove the view at the specified index - * - * @param index The index of the view to remove - */ - void removeView(int index); - - /** - * Remove the given if it exists in this group - * - * @param view The view to remove - */ - void removeView(IView view); - - /** Remove all children in this group */ - void removeAll(); - - /** - * Perform an action in every child in this group - * - * @param consumer The consumer which will consume the views - */ - void forEachChild(Consumer consumer); - - /** - * Get the number of child views this layout contains - * - * @return The child count - */ - int getChildCount(); - - /** - * Get the list of children in this group - * - * @return The list of children - */ - List getChildren(); - - /** - * Get child at the provided index - * - * @param index The index of child - * @throws IndexOutOfBoundsException If there is no child at the given index - */ - IView getChildAt(int index); - - /** - * Find the index of the given children in this group. - * - * @param view The view to find index of. - * @return The index of the child or -1 if this group does not contain the given child. - */ - int indexOfChild(IView view); - - /** - * Find the index of the given children in this group. - * - * @param view The view to find index of. - * @return The index of the child or -1 if this group does not contain the given child. - */ - int indexOfChild(View view); - - /** - * Register the given hierarchy change listener to this group. - * - * @param listener The listener to register. - */ - void registerHierarchyChangeListener(OnHierarchyChangeListener listener); - - /** - * Unregister the given hierarchy change listener from this view group. - * - * @param listener The listener to unregister. - */ - void unregisterHierarchyChangeListener(OnHierarchyChangeListener listener); - - /** - * A listener which can be used to get notification when a children is added or removed from an - * {@link IViewGroup}. - * - * @author Akash Yadav - */ - public static interface OnHierarchyChangeListener { - - /** - * Called when a child view is added in the view group to which this listener is attached. - * - * @param view The view that was added. - */ - void onViewAdded(IView view); - - /** - * Called when a child view is removed from the view group to which this listener is attached. - * - * @param view The view that was removed. Note that this view's parent will be null. - */ - void onViewRemoved(IView view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/InflateException.java b/xml-inflater/src/main/java/com/itsaky/inflater/InflateException.java deleted file mode 100644 index ba23d584b..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/InflateException.java +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -/** Thrown when there was an error inflating the layout */ -public class InflateException extends RuntimeException { - - public InflateException(String message) { - super(message); - } - - public InflateException(Throwable th) { - super(th); - } - - public InflateException(String msg, Throwable th) { - super(msg, th); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/LayoutInflaterConfiguration.java b/xml-inflater/src/main/java/com/itsaky/inflater/LayoutInflaterConfiguration.java deleted file mode 100644 index 905417d99..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/LayoutInflaterConfiguration.java +++ /dev/null @@ -1,87 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater; - -import com.itsaky.attrinfo.AttrInfo; -import com.itsaky.widgets.WidgetInfo; - -import java.io.File; -import java.util.Set; - -/** Configuration for {@link ILayoutInflater} */ -public class LayoutInflaterConfiguration { - - final Set resDirs; - final AttrInfo attrInfo; - final WidgetInfo widgetInfo; - final IResourceTable resFinder; - final ILayoutInflater.ContextProvider contextProvider; - - public LayoutInflaterConfiguration( - Set resDirs, - AttrInfo attrInfo, - WidgetInfo widgetInfo, - IResourceTable resourceProvider, - ILayoutInflater.ContextProvider contextProvider) { - this.resDirs = resDirs; - this.attrInfo = attrInfo; - this.widgetInfo = widgetInfo; - this.resFinder = resourceProvider; - this.contextProvider = contextProvider; - } - - /** A class that builds a {@link LayoutInflaterConfiguration} */ - public static class Builder { - - private Set resDirs; - private AttrInfo attrInfo; - private WidgetInfo widgetInfo; - private IResourceTable resourceProvider; - private ILayoutInflater.ContextProvider provider; - - public Builder setResourceDirectories(Set dirs) { - this.resDirs = dirs; - return this; - } - - public Builder setAttrInfo(AttrInfo info) { - this.attrInfo = info; - return this; - } - - public Builder setWidgetInfo(WidgetInfo info) { - this.widgetInfo = info; - return this; - } - - public Builder setResourceFinder(IResourceTable provider) { - this.resourceProvider = provider; - return this; - } - - public Builder setContextProvider(ILayoutInflater.ContextProvider provider) { - this.provider = provider; - return this; - } - - public LayoutInflaterConfiguration create() { - return new LayoutInflaterConfiguration( - resDirs, attrInfo, widgetInfo, resourceProvider, provider); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/NotSupportedException.java b/xml-inflater/src/main/java/com/itsaky/inflater/NotSupportedException.java deleted file mode 100644 index 9efa61b87..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/NotSupportedException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater; - -/** - * Exception thrown by inflaters and drawable parsers to indicate that the a specific view or tag is - * not supported. - * - * @author Akash Yadav - */ -public class NotSupportedException extends UnsupportedOperationException { - - public NotSupportedException() {} - - public NotSupportedException(String message) { - super(message); - } - - public NotSupportedException(String message, Throwable cause) { - super(message, cause); - } - - public NotSupportedException(Throwable cause) { - super(cause); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java deleted file mode 100644 index 25895d92a..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater; - -import static com.itsaky.inflater.util.Preconditions.assertNotBlank; -import static com.itsaky.inflater.util.Preconditions.assertNotnull; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; - -import com.blankj.utilcode.util.ReflectUtils; -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.attrinfo.AttrInfo; -import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; -import com.itsaky.inflater.impl.BaseView; -import com.itsaky.inflater.impl.ErrorUiView; -import com.itsaky.inflater.impl.IncludeLayout; -import com.itsaky.inflater.impl.UiAttribute; -import com.itsaky.inflater.impl.UiView; -import com.itsaky.inflater.impl.UiViewGroup; -import com.itsaky.inflater.util.Preconditions; -import com.itsaky.widgets.WidgetInfo; -import com.itsaky.widgets.models.Widget; -import com.itsaky.xml.INamespace; -import com.itsaky.xml.impl.UiNamespace; - -import org.jsoup.Jsoup; -import org.jsoup.nodes.Attribute; -import org.jsoup.nodes.Attributes; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.parser.Parser; - -import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.util.Objects; -import java.util.Set; -import java.util.regex.Pattern; - -class XMLLayoutInflater extends BaseLayoutInflater { - - public static final String ATTR_ADAPTER_SUFFIX = "AttrAdapter"; - private static final ILogger LOG = ILogger.newInstance("XMLLayoutInflater"); - private final Set resDirs; - private final AttrInfo attrInfo; - private final WidgetInfo widgetInfo; - private final IResourceTable resFinder; - private ContextProvider contextProvider; - - XMLLayoutInflater(@NonNull LayoutInflaterConfiguration config) { - this.resDirs = config.resDirs; - this.attrInfo = config.attrInfo; - this.widgetInfo = config.widgetInfo; - this.resFinder = config.resFinder; - this.contextProvider = config.contextProvider; - - Preconditions.assertAllNotNull( - "LayoutInflater parameters cannot be null", resDirs, attrInfo, widgetInfo, resFinder); - } - - @Override - public void resetContextProvider(ILayoutInflater.ContextProvider provider) { - this.contextProvider = provider; - } - - @NonNull - @Override - protected IResourceTable requireResourceTable() { - return this.resFinder; - } - - @Override - @NonNull - protected IView doInflate(String layout, ViewGroup parent) throws InflateException { - try { - - IDTable.newRound(); - - // Notify - preInflate(); - - final Document doc = Jsoup.parse(layout, "", Parser.xmlParser()); - - if (doc.childrenSize() <= 0) { - throw new InflateException("No views added"); - } - - if (doc.childrenSize() > 1) { - throw new InflateException( - "More than one root element was found. An XML layout can have only one root" - + " element."); - } - - IView root = onCreateView(doc.child(0), parent, null); - - if (root == null) { - root = onCreateErrorView(TextView.class.getName(), "No views added", parent); - } - - // Notify - postInflate(root); - - return root; - } catch (Throwable th) { - throw new InflateException("Unable to inflate layout", th); - } - } - - protected IView onCreateView(Element tag, ViewGroup parentGroup, IViewGroup parent) - throws InflateException { - if (tag == null) { - return null; - } - - final String name = tag.tagName().trim(); - - if (name.equals("include")) { - return createFromInclude(tag.attributes(), parent); - } - - final IView root; - int style = tag.hasAttr("style") ? parseFrameworkStyle(tag.attr("style")) : -1; - if (!name.contains(".")) { - root = createFromSimpleName(name, parentGroup, style); - } else { - root = createFromQualifiedName(name, parentGroup, style); - } - - if (root == null) { - return null; - } - - registerAttributeAdaptersTo(root); - - if (!tag.attributes().isEmpty()) { - addAttributesTo(root, tag.attributes(), parent); - } - - if (root instanceof IViewGroup) { - addChildren(tag, (IViewGroup) root); - } - - postCreateView(root); - - return root; - } - - protected void addChildren(@NonNull Element tag, IViewGroup parent) { - if (tag.childrenSize() > 0 && !parent.isPlaceholder()) { - for (Element child : tag.children()) { - final IView created = onCreateView(child, (ViewGroup) parent.asView(), parent); - if (created != null) { - parent.addView(created); - created.setParent(parent); - } - } - } - } - - protected int parseFrameworkStyle(String value) throws InflateException { - try { - final String name = underscorize(value.substring(value.indexOf('/') + 1)); - return (int) android.R.attr.class.getField(name).get(null); - } catch (Throwable th) { - LOG.error("Unable to parse framework style", th); - return -1; - } - } - - protected void registerAttributeAdaptersTo(@NonNull IView root) { - root.registerAttributeAdapter(onCreateAttributeAdapter(root.asView())); - } - - protected IAttributeAdapter onCreateAttributeAdapter(View view) { - final var displayMetrics = contextProvider.getContext().getResources().getDisplayMetrics(); - try { - final String adapterName = view.getClass().getName().concat(ATTR_ADAPTER_SUFFIX); - final String name = "com.itsaky.inflater.adapters.".concat(adapterName); - final Class adapterClass = - getClass().getClassLoader().loadClass(name).asSubclass(IAttributeAdapter.class); - final var constructor = - adapterClass.getConstructor(IResourceTable.class, DisplayMetrics.class); - final var adapter = constructor.newInstance(resFinder, displayMetrics); - adapter.setResourceFinder(resFinder); - return adapter; - } catch (Throwable th) { - LOG.error( - BaseApplication.getBaseInstance() - .getString( - com.itsaky.androidide.resources.R.string.err_no_attr_adapter, view.getClass().getName()), - th); - } - - // If we cannot find a suitable adapter, fall back to using common adapters - IAttributeAdapter adapter; - if (view instanceof ViewGroup) { - adapter = new ViewGroupAttrAdapter(resFinder, displayMetrics); - } else { - adapter = new ViewAttrAdapter(resFinder, displayMetrics); - } - return adapter; - } - - protected void addAttributesTo(IView view, Attributes attributes, IViewGroup parent) { - assertNotnull(attributes, "Cannot apply null attributes!"); - - for (Attribute attr : attributes) { - final String key = attr.getKey(); - final String[] split = - !key.contains(":") ? new String[] {"", key} : key.split(Pattern.quote(":"), 2); - final String namespace = split[0]; - final String name = split[1]; - final String value = attr.getValue(); - - INamespace ns; - if (namespace.equals("xmlns")) { - ns = INamespace.DECLARATOR; - view.registerNamespace(new UiNamespace(name, value)); - } else { - // set the parent so that we could look for - // declared namespaces - view.setParent(parent); - ns = view.findRegisteredNamespace(namespace); - } - - if (ns == null) { - ns = INamespace.invalid(namespace); - } - - final IAttribute iAttr = asAttribute(ns, name, value); - view.addAttribute(iAttr); - - postApplyAttribute(iAttr, view); - } - } - - protected IAttribute asAttribute(INamespace namespace, String name, String value) { - return new UiAttribute(namespace, name, value); - } - - protected IView create(@NonNull String name, ViewGroup parent, int style) - throws InflateException { - if (!name.contains(".")) { - return createFromSimpleName(name, parent, style); - } else { - return createFromQualifiedName(name, parent, style); - } - } - - protected IView createFromInclude(Attributes attrs, IViewGroup parent) { - - if (!attrs.hasKey("layout")) { - throw new InflateException(" tag doesn't have 'layout' attribute."); - } - - final var layout = attrs.get("layout"); - Preconditions.assertNotBlank( - layout, - "'layout' attribute for tag has invalid value." + "layout='" + layout + "'"); - - if (layout.startsWith("@android:layout/")) { - return inflateAndroidLayout(layout.substring("@android:layout/".length()), attrs, parent); - } - - final var name = layout.substring("@layout/".length()); - final var file = resFinder.findLayout(name); - if (file == null) { - throw new InflateException("Layout file '" + name + "' not found"); - } - - return inflateLocalIncludeLayout(attrs, parent, name, file); - } - - protected IView inflateAndroidLayout(String name, Attributes attrs, @NonNull IViewGroup parent) { - final var id = (int) ReflectUtils.reflect(android.R.layout.class).field(name).get(); - final var view = LayoutInflater.from(contextProvider.getContext()).inflate(id, null); - final var layout = new IncludeLayout(new UiView(IncludeLayout.TAG, view, false)); - - applyLayoutParams(layout, (ViewGroup) parent.asView()); - addAttributesTo(layout, attrs, parent); - - return layout; - } - - protected IView createFromSimpleName(String name, ViewGroup parent, int style) - throws InflateException { - final Widget widget = widgetInfo.getWidgetBySimpleName(name); - if (widget == null) { - LOG.error("Unable to inflate view. widget == null"); - return onCreateErrorView( - name, getString(com.itsaky.androidide.resources.R.string.msg_cannot_create_view, name), parent); - } - - return createFromQualifiedName(widget.name, parent, style); - } - - protected IView createFromQualifiedName(String name, ViewGroup parent, int style) - throws InflateException { - assertNotBlank(name, "Invalid tag name: " + name); - - // TODO Try to load classes directly from .class files if possible - try { - - final var androidView = this.widgetInfo.getWidget(name); - if (androidView == null) { - // If this is not an Android view, do not bother to create one - throw new NotSupportedException(); - } - - final View created = createAndroidViewForName(name, style); - final BaseView view = - created instanceof ViewGroup - ? new UiViewGroup(name, (ViewGroup) created) - : new UiView(name, created); - return applyLayoutParams(view, parent); - } catch (Throwable th) { - - if (th instanceof NotSupportedException) { - return onCreateErrorView(name, getString(R.string.msg_view_not_supported, name), parent); - } - - final var msg = getString(R.string.msg_cannot_create_view, name); - LOG.error(msg, th); - return onCreateErrorView(name, msg, parent); - } - } - - @NonNull - protected View createAndroidViewForName(String name, int style) - throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, - InstantiationException, java.lang.reflect.InvocationTargetException { - final Class loaded = Class.forName(name).asSubclass(View.class); - final Constructor constructor; - if (style == -1) { - constructor = loaded.getConstructor(Context.class); - return constructor.newInstance(contextProvider.getContext()); - } else { - constructor = loaded.getConstructor(Context.class, AttributeSet.class, int.class); - return constructor.newInstance(contextProvider.getContext(), null, style); - } - } - - protected IView applyLayoutParams(@NonNull IView view, ViewGroup parent) { - final ViewGroup.LayoutParams params = generateLayoutParams(parent); - view.asView().setLayoutParams(params); - return view; - } - - protected IView onCreateErrorView(String name, String msg, final ViewGroup parent) - throws InflateException { - final var view = - ErrorUiView.create( - Objects.requireNonNull(contextProvider.getContext(), "Context is null."), name, msg); - return applyLayoutParams(view, parent); - } - - @NonNull - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup parent) { - try { - Class clazz = parent.getClass(); - Method method = null; - - while (true) { - try { - method = clazz.getDeclaredMethod("generateDefaultLayoutParams"); - break; - } catch (Throwable e) { - /* ignored */ - } - clazz = clazz.getSuperclass(); - - if (clazz == null) { - break; - } - } - - if (method != null) { - method.setAccessible(true); - return (ViewGroup.LayoutParams) method.invoke(parent); - } - - LOG.error("Unable to create default params for view parent:", parent); - return new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } catch (Throwable th) { - throw new InflateException("Unable to create layout params for parent: " + parent, th); - } - } - - @NonNull - private String underscorize(@NonNull String substring) { - return substring.replace(".", "_"); - } - - @NonNull - private IncludeLayout inflateLocalIncludeLayout( - Attributes attrs, IViewGroup parent, String name, File file) { - - // DesignerActivity applies drag listeners in 'onInflateView()' call - // But, layouts must not be edited here - // So, we avoid notifying the listeners - super.notify = false; - - final var view = this.inflate(file, (ViewGroup) parent.asView()); - Preconditions.assertNotnull(view, "Failed to inflate '" + name + "'"); - - final var includeLayout = new IncludeLayout(view); - - applyLayoutParams(includeLayout, (ViewGroup) parent.asView()); - addAttributesTo(includeLayout, attrs, parent); - - // Enable notifications - super.notify = true; - - return includeLayout; - } - - private String getString(@StringRes int id, Object... format) throws InflateException { - assertNotnull(contextProvider.getContext(), "Context is null!"); - return contextProvider.getContext().getString(id, format); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/README.md b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/README.md deleted file mode 100644 index 06f82aba4..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Attribute Adapters - -Attribute adapters handle attributes for a specific view. - -## Implementing an Adapter - -### Basic - -To implment an adapter, you'll have to write a class extending one of the existing adapters. - -Adapters extend other adapters in the same way as Android's view classes extend other views. For -example, ```android.widget.CompoundButton``` extends ```android.widget.Button```, in the same way -```com.itsaky.layoutinflater.adapters.android.widget.CompoundButtonAttrAdapter``` extends -```com.itsaky.layoutinflater.adapters.android.widget.ButtonAttrAdapter```. In this way, we don't -have to rewrite whole logic again to implement adapters for every other widget. - -### Naming an adapter - -If you look at the package name and the simple name of the adapter classes mentioned above, you'll -find that there is a specific way an adapter is named. - -An adpater should be named in the following way : -```com.itsaky.layoutinflater.adapters..AttrAdapter```, -where, `````` is the package name of the widget you are implementing an adapter for -and `````` is the simple name of the widget. The simple name is then suffixed -with ```AttrAdapter```. - -Here is an example : -Assume we have to implement an adapter for ```android.widget.Button```. Here, `````` -is ```android.widget``` and -`````` is ```Button```. - -So, the adapter implentation class should -be ```com.itsaky.layoutinflater.adapters.android.widget.ButtonAttrAdapter```. - -### Why such naming? - -We use reflection to automatically create an adapter when inflating a view. For example, -if ```android.widget.Button``` instance is being created, we could simply create an adapter instance -using reflection by getting the package name and simple name of the widget. - -See ```XMLLayoutInflater#onCreateAttributeAdapter(View)```. - -### Precautions - -- Make sure you correctly spell the file names. As you might already know, file names are case - sensitive. -- Some methods (or even classes) are not available for earlier versions of Android. The classes or - methods you're using must be compatible with API 24. - -You could also check if you can use a method. Some methods are already implemented in -ViewAttrAdapter so you won't have to rewrite them. For example: - -``` -isApi26() // Android 8 (Oreo) -isApi28() // Android 9 (Pie) -isApi29() // Android 10 (Q) -isApi30() // Android 11 (R) -``` diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/gesture/GestureOverlayViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/gesture/GestureOverlayViewAttrAdapter.java deleted file mode 100644 index 639af086b..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/gesture/GestureOverlayViewAttrAdapter.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.gesture; - -import android.gesture.GestureOverlayView; -import android.util.DisplayMetrics; -import android.view.View; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.ReflectUtils; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.widget.FrameLayoutAttrAdapter; - -/** - * Attribute adapter for {@link GestureOverlayView} - * - * @author Akash Yadav - */ -public class GestureOverlayViewAttrAdapter extends FrameLayoutAttrAdapter { - - public GestureOverlayViewAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof GestureOverlayView; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var gesture = (GestureOverlayView) view; - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "eventsInterceptionEnabled": - gesture.setEventsInterceptionEnabled(parseBoolean(value)); - break; - case "fadeDuration": - ReflectUtils.reflect(gesture).field("mFadeDuration", parseLong(value, 150)); - break; - case "fadeEnabled": - gesture.setFadeEnabled(parseBoolean(value)); - break; - case "fadeOffset": - gesture.setFadeOffset(parseLong(value, 420)); - break; - case "gestureColor": - gesture.setGestureColor(parseColor(value, gesture.getContext())); - break; - case "gestureStrokeAngleThreshold": - gesture.setGestureStrokeAngleThreshold(parseFloat(value)); - break; - case "gestureStrokeLengthThreshold": - gesture.setGestureStrokeLengthThreshold(parseFloat(value)); - break; - case "gestureStrokeSquarenessThreshold": - gesture.setGestureStrokeSquarenessTreshold(parseFloat(value)); - break; - case "gestureStrokeType": - gesture.setGestureStrokeType(parseGestureStrokeType(value)); - break; - case "gestureStrokeWidth": - gesture.setGestureStrokeWidth(parseFloat(value)); - break; - case "orientation": - gesture.setOrientation(parseOrientation(value)); - break; - case "uncertainGestureColor": - gesture.setUncertainGestureColor(parseColor(value, gesture.getContext())); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - private int parseOrientation(String value) { - if ("horizontal".equals(value)) { - return GestureOverlayView.ORIENTATION_HORIZONTAL; - } - - return GestureOverlayView.ORIENTATION_VERTICAL; - } - - protected int parseGestureStrokeType(String value) { - if ("multiple".equals(value)) { - return GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE; - } - return GestureOverlayView.GESTURE_STROKE_TYPE_SINGLE; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java deleted file mode 100644 index c073a7571..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater.adapters.android.view; - -import android.content.Context; -import android.graphics.PorterDuff; -import android.os.Build; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; - -import androidx.annotation.ChecksSdkIntAtLeast; -import androidx.annotation.NonNull; - -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IAttributeAdapter; -import com.itsaky.inflater.IDTable; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.util.CommonParseUtils; -import com.itsaky.xml.INamespace; - -/** - * Handles attributes common to all android views. Also, LayoutParams related issues are also - * handled by this adapter. - * - * @author Akash Yadav - */ -public class ViewAttrAdapter extends CommonParseUtils implements IAttributeAdapter { - - protected static final ILogger LOG = ILogger.newInstance("BaseViewAttrAdapter"); - - public ViewAttrAdapter(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public void setResourceFinder(IResourceTable resourceFinder) { - this.resourceFinder = resourceFinder; - } - - @Override - public boolean isApplicableTo(View view) { - return true; // Can be applied to any view - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final INamespace namespace = attribute.getNamespace(); - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - final ViewGroup.LayoutParams params = view.getLayoutParams(); - final Context context = view.getContext(); - - if (!canHandleNamespace(namespace)) { - return false; - } - - boolean handled = true; - switch (name) { - case "layout_height": - params.height = parseDimension(value, -2, displayMetrics); - break; - case "layout_width": - params.width = parseDimension(value, -2, displayMetrics); - break; - case "alpha": - view.setAlpha(parseFloat(value)); - break; - case "background": - view.setBackground(parseDrawable(value, context)); - break; - case "backgroundTint": - view.setBackgroundTintList(parseColorStateList(value, context)); - break; - case "backgroundTintMode": - view.setBackgroundTintMode(parsePorterDuffMode(value)); - break; - case "clipToOutline": - view.setClipToOutline(parseBoolean(value)); - break; - case "contentDescription": - view.setContentDescription(parseString(value)); - break; - case "contextClickable": - view.setContextClickable(parseBoolean(value)); - break; - case "defaultFocusHighlightEnabled": - view.setDefaultFocusHighlightEnabled(parseBoolean(value)); - break; - case "drawingCacheQuality": - view.setDrawingCacheQuality(parseDrawingCacheQuality(value)); - break; - case "duplicateParentState": - view.setDuplicateParentStateEnabled(parseBoolean(value)); - break; - case "elevation": - view.setElevation(parseDimension(value, 0, displayMetrics)); - break; - case "fadeScrollbars": - view.setScrollbarFadingEnabled(parseBoolean(value)); - break; - case "fadingEdgeLength": - view.setFadingEdgeLength(parseDimension(value, 0, displayMetrics)); - break; - case "filterTouchesWhenObscured": - view.setFilterTouchesWhenObscured(parseBoolean(value)); - break; - case "foreground": - view.setForeground(parseDrawable(value, context)); - break; - case "foregroundGravity": - view.setForegroundGravity(parseGravity(value)); - break; - case "foregroundTint": - view.setForegroundTintList(parseColorStateList(value, context)); - break; - case "foregroundTintMode": - view.setForegroundTintMode(parsePorterDuffMode(value)); - break; - case "id": - view.setId(parseId(value)); - break; - case "minHeight": - view.setMinimumHeight(parseDimension(value, 0, displayMetrics)); - break; - case "minWidth": - view.setMinimumWidth(parseDimension(value, 0, displayMetrics)); - break; - case "padding": - final int padding = parseDimension(value, 0, displayMetrics); - view.setPaddingRelative(padding, padding, padding, padding); - break; - case "paddingLeft": - final int paddingLeft = parseDimension(value, 0, displayMetrics); - view.setPadding( - paddingLeft, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom()); - break; - case "paddingTop": - final int paddingTop = parseDimension(value, 0, displayMetrics); - view.setPadding( - view.getPaddingLeft(), paddingTop, view.getPaddingRight(), view.getPaddingBottom()); - break; - case "paddingRight": - final int paddingRight = parseDimension(value, 0, displayMetrics); - view.setPadding( - view.getPaddingLeft(), view.getPaddingTop(), paddingRight, view.getPaddingBottom()); - break; - case "paddingBottom": - final int paddingBottom = parseDimension(value, 0, displayMetrics); - view.setPadding( - view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), paddingBottom); - break; - case "paddingStart": - final int paddingStart = parseDimension(value, 0, displayMetrics); - view.setPaddingRelative( - paddingStart, view.getPaddingTop(), view.getPaddingEnd(), view.getPaddingBottom()); - break; - case "paddingEnd": - final int paddingEnd = parseDimension(value, 0, displayMetrics); - view.setPaddingRelative( - view.getPaddingStart(), view.getPaddingTop(), paddingEnd, view.getPaddingBottom()); - break; - case "rotation": - view.setRotation(parseFloat(value)); - break; - case "rotationX": - view.setRotationX(parseFloat(value)); - break; - case "rotationY": - view.setRotationY(parseFloat(value)); - break; - case "scaleX": - view.setScaleX(parseFloat(value)); - break; - case "scaleY": - view.setScaleY(parseFloat(value)); - break; - case "scrollX": - view.setScrollX((int) parseFloat(value)); - break; - case "scrollY": - view.setScrollY((int) parseFloat(value)); - break; - case "textAlignment": - view.setTextAlignment(parseTextAlignment(value)); - break; - case "textDirection": - view.setTextDirection(parseTextDirection(value)); - break; - case "tooltipText": - view.setTooltipText(parseString(value)); - break; - case "transformPivotX": - view.setPivotX(parseFloat(value)); - break; - case "transformPivotY": - view.setPivotY(parseFloat(value)); - break; - case "translationX": - view.setTranslationX(parseFloat(value)); - break; - case "translationY": - view.setTranslationY(parseFloat(value)); - break; - case "translationZ": - view.setTranslationZ(parseFloat(value)); - break; - case "visibility": - view.setVisibility(parseVisibility(value)); - break; - default: - handled = false; - break; - } - - // ----- Handle attributes related to parent ------ - - if (!handled && params instanceof LinearLayout.LayoutParams) { - handled = - handleLinearLayoutParams((LinearLayout.LayoutParams) params, name, value, displayMetrics); - } - - if (!handled && params instanceof RelativeLayout.LayoutParams) { - handled = handleRelativeLayoutParams((RelativeLayout.LayoutParams) params, name, value); - } - - if (!handled && params instanceof FrameLayout.LayoutParams) { - handled = handleFrameLayoutParams((FrameLayout.LayoutParams) params, name, value); - } - - if (!handled && params instanceof ViewGroup.MarginLayoutParams) { - handled = - handleMarginParams((ViewGroup.MarginLayoutParams) params, name, value, displayMetrics); - } - - view.setLayoutParams(params); - - return handled; - } - - protected int parseId(@NonNull String value) { - if (value.startsWith("@id/")) { - final String name = value.substring("@id/".length()); - return IDTable.getId(name); - } else if (value.startsWith("@+id/")) { - final String name = value.substring("@+id/".length()); - return IDTable.newId(name); - } - return View.NO_ID; - } - - protected boolean handleRelativeLayoutParams( - RelativeLayout.LayoutParams params, @NonNull String name, String value) { - boolean handled = true; - switch (name) { - case "layout_above": - params.addRule(RelativeLayout.ABOVE, parseId(value)); - break; - case "layout_alignBaseline": - params.addRule(RelativeLayout.ALIGN_BASELINE, parseId(value)); - break; - case "layout_alignBottom": - params.addRule(RelativeLayout.ALIGN_BOTTOM, parseId(value)); - break; - case "layout_alignEnd": - params.addRule(RelativeLayout.ALIGN_END, parseId(value)); - break; - case "layout_alignLeft": - params.addRule(RelativeLayout.ALIGN_LEFT, parseId(value)); - break; - case "layout_alignParentTop": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_TOP, params); - break; - case "layout_alignParentBottom": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_BOTTOM, params); - break; - case "layout_alignParentStart": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_START, params); - break; - case "layout_alignParentEnd": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_END, params); - break; - case "layout_alignParentLeft": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_LEFT, params); - break; - case "layout_alignParentRight": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.ALIGN_PARENT_RIGHT, params); - break; - case "layout_alignRight": - params.addRule(RelativeLayout.ALIGN_RIGHT, parseId(value)); - break; - case "layout_alignStart": - params.addRule(RelativeLayout.ALIGN_START, parseId(value)); - break; - case "layout_alignTop": - params.addRule(RelativeLayout.ALIGN_TOP, parseId(value)); - break; - case "layout_alignWithParentIfMissing": - params.alignWithParent = parseBoolean(value); - break; - case "layout_below": - params.addRule(RelativeLayout.BELOW, parseId(value)); - break; - case "layout_centerHorizontal": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.CENTER_HORIZONTAL, params); - break; - case "layout_centerInParent": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.CENTER_IN_PARENT, params); - break; - case "layout_centerVertical": - setRelativeRuleIfTrue(parseBoolean(value), RelativeLayout.CENTER_VERTICAL, params); - break; - case "layout_toEndOf": - params.addRule(RelativeLayout.END_OF, parseId(value)); - break; - case "layout_toStartOf": - params.addRule(RelativeLayout.START_OF, parseId(value)); - break; - case "layout_toLeftOf": - params.addRule(RelativeLayout.LEFT_OF, parseId(value)); - break; - case "layout_toRightOf": - params.addRule(RelativeLayout.RIGHT_OF, parseId(value)); - break; - default: - handled = false; - } - return handled; - } - - protected boolean handleLinearLayoutParams( - LinearLayout.LayoutParams params, @NonNull String name, String value, DisplayMetrics dm) { - boolean handled = true; - switch (name) { - case "layout_gravity": - params.gravity = parseGravity(value); - break; - case "layout_weight": - params.weight = parseDimension(value, 1, dm); - break; - default: - handled = false; - break; - } - return handled; - } - - protected boolean handleFrameLayoutParams( - FrameLayout.LayoutParams params, @NonNull String name, String value) { - boolean handled = true; - - if ("layout_gravity".equals(name)) { - params.gravity = parseGravity(value); - } else { - handled = false; - } - - return handled; - } - - protected boolean handleMarginParams( - ViewGroup.MarginLayoutParams params, @NonNull String name, String value, DisplayMetrics dm) { - boolean handled = true; - switch (name) { - case "layout_margin": - final int margin = parseDimension(value, 0, dm); - params.setMargins(margin, margin, margin, margin); - break; - case "layout_marginLeft": - params.leftMargin = parseDimension(value, 0, dm); - break; - case "layout_marginTop": - params.topMargin = parseDimension(value, 0, dm); - break; - case "layout_marginRight": - params.rightMargin = parseDimension(value, 0, dm); - break; - case "layout_marginBottom": - params.bottomMargin = parseDimension(value, 0, dm); - break; - case "layout_marginStart": - params.setMarginStart(parseDimension(value, 0, dm)); - break; - case "layout_marginEnd": - params.setMarginEnd(parseDimension(value, 0, dm)); - break; - default: - handled = false; - break; - } - - return handled; - } - - protected boolean canHandleNamespace(@NonNull IAttribute attr) { - return canHandleNamespace(attr.getNamespace()); - } - - protected boolean canHandleNamespace(INamespace namespace) { - return INamespace.ANDROID.equals(namespace); - } - - protected int parseDrawingCacheQuality(@NonNull String value) { - switch (value) { - case "high": - return View.DRAWING_CACHE_QUALITY_HIGH; - case "low": - return View.DRAWING_CACHE_QUALITY_LOW; - case "auto": - default: - return View.DRAWING_CACHE_QUALITY_AUTO; - } - } - - protected int parseVisibility(@NonNull String value) { - switch (value) { - case "gone": - return View.GONE; - case "invisible": - return View.INVISIBLE; - case "visible": - default: - return View.VISIBLE; - } - } - - protected int parseTextAlignment(@NonNull String value) { - switch (value) { - case "center": - return View.TEXT_ALIGNMENT_CENTER; - case "gravity": - return View.TEXT_ALIGNMENT_GRAVITY; - case "textEnd": - return View.TEXT_ALIGNMENT_TEXT_END; - case "textStart": - return View.TEXT_ALIGNMENT_TEXT_START; - case "viewEnd": - return View.TEXT_ALIGNMENT_VIEW_END; - case "viewStart": - return View.TEXT_ALIGNMENT_VIEW_START; - case "inherit": - default: - return View.TEXT_ALIGNMENT_INHERIT; - } - } - - protected int parseTextDirection(@NonNull String value) { - switch (value) { - case "anyRtl": - return View.TEXT_DIRECTION_ANY_RTL; - case "firstStrong": - return View.TEXT_DIRECTION_FIRST_STRONG; - case "firstStrongLtr": - return View.TEXT_DIRECTION_FIRST_STRONG_LTR; - case "firstStrongRtl": - return View.TEXT_DIRECTION_FIRST_STRONG_RTL; - case "locale": - return View.TEXT_DIRECTION_LOCALE; - case "ltr": - return View.TEXT_DIRECTION_LTR; - case "rtl": - return View.TEXT_DIRECTION_RTL; - case "inherit": - default: - return View.TEXT_DIRECTION_INHERIT; - } - } - - protected PorterDuff.Mode parsePorterDuffMode(@NonNull String mode) { - switch (mode) { - case "add": - return PorterDuff.Mode.ADD; - case "multiply": - return PorterDuff.Mode.MULTIPLY; - case "screen": - return PorterDuff.Mode.SCREEN; - case "src_atop": - return PorterDuff.Mode.SRC_ATOP; - case "src_in": - return PorterDuff.Mode.SRC_IN; - case "src_over": - return PorterDuff.Mode.SRC_OVER; - default: - return PorterDuff.Mode.SRC; - } - } - - protected float parseFloat(String value) { - try { - return Float.parseFloat(value); - } catch (Throwable th) { - return 1f; - } - } - - protected float parseFloat(String value, float def) { - try { - return Float.parseFloat(value); - } catch (Throwable th) { - return def; - } - } - - protected long parseLong(String value, long def) { - try { - return Long.parseLong(value); - } catch (Throwable th) { - return def; - } - } - - protected int parseDimension(final String value, int defaultValue, final DisplayMetrics dm) { - - if (value == null) { - return defaultValue; - } - - char c = value.charAt(0); - if (Character.isDigit(c)) { - // A dimension value which starts with a digit. E.g.: 1dp, 12sp, 123px, etc. - StringBuilder dimensionVal = new StringBuilder(); - int index = 0; - while (index < value.length() && Character.isDigit(c = value.charAt(index))) { - dimensionVal.append(c); - index++; - } - - final int dimen = Integer.parseInt(dimensionVal.toString()); - final String dimensionType = value.substring(index); - return (int) TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, dm); - } else if (c == '@') { - String name = value.substring("@dimen/".length()); - return parseDimension(resourceFinder.findDimension(name), defaultValue, dm); - } else if (Character.isLetter(c)) { - // This could be one of the following : - // 1. match_parent - // 2. wrap_content - // 3. fill_parent - switch (value) { - case "match_parent": - case "fill_parent": - return ViewGroup.LayoutParams.MATCH_PARENT; - case "wrap_content": - default: - return ViewGroup.LayoutParams.WRAP_CONTENT; - } - } - - return defaultValue; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) - protected boolean isApi28() { - return Build.VERSION.SDK_INT >= 28; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) - protected boolean isApi29() { - return Build.VERSION.SDK_INT >= 29; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) - protected boolean isApi30() { - return Build.VERSION.SDK_INT >= 30; - } - - private void setRelativeRuleIfTrue( - boolean condition, int rule, RelativeLayout.LayoutParams params) { - if (condition) { - params.addRule(rule); - } else { - params.removeRule(rule); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java deleted file mode 100644 index 747c51b52..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java +++ /dev/null @@ -1,111 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater.adapters.android.view; - -import android.animation.LayoutTransition; -import android.util.DisplayMetrics; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -import org.jetbrains.annotations.Contract; - -/** - * An attribute adapter implementation for ViewGroups. - * - * @author Akash Yadav - */ -public class ViewGroupAttrAdapter extends ViewAttrAdapter { - - public ViewGroupAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof ViewGroup; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - - final ViewGroup group = (ViewGroup) view; - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "animateLayoutChanges": - group.setLayoutTransition(new LayoutTransition()); - break; - case "clipChildren": - group.setClipChildren(parseBoolean(value)); - break; - case "clipToPadding": - group.setClipToPadding(parseBoolean(value)); - break; - case "descendantFocusability": - group.setDescendantFocusability(parseDescendantsFocusability(value)); - break; - case "layoutMode": - group.setLayoutMode(parseLayoutMode(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - @Contract(pure = true) - private int parseLayoutMode(String value) { - switch (value) { - case "opticalBounds": - return ViewGroup.LAYOUT_MODE_OPTICAL_BOUNDS; - case "clipBounds": - default: - return ViewGroup.LAYOUT_MODE_CLIP_BOUNDS; - } - } - - protected int parseDescendantsFocusability(@NonNull String value) { - switch (value) { - case "beforeDescendants": - return ViewGroup.FOCUS_BEFORE_DESCENDANTS; - case "blocksDescendants": - return ViewGroup.FOCUS_BLOCK_DESCENDANTS; - case "afterDescendants": - default: - return ViewGroup.FOCUS_AFTER_DESCENDANTS; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsListViewAttrAdapter.kt b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsListViewAttrAdapter.kt deleted file mode 100644 index 314a8ba73..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsListViewAttrAdapter.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget - -import android.util.DisplayMetrics -import android.view.View -import android.widget.AbsListView -import com.itsaky.inflater.IAttribute -import com.itsaky.inflater.IResourceTable - -/** - * Attribute adapter for the [AbsListView] widget. - * - * @author Akash Yadav - */ -open class AbsListViewAttrAdapter(resourceTable: IResourceTable, displayMetrics: DisplayMetrics) : - AdapterViewAttrAdapter(resourceTable, displayMetrics) { - - override fun isApplicableTo(view: View?): Boolean { - return view is AbsListView - } - - override fun apply(attribute: IAttribute, view: View): Boolean { - val list = view as AbsListView - val context = list.context - val value = attribute.value - - if (!canHandleNamespace(attribute)) { - return false - } - - var handled = true - when (attribute.attributeName) { - "cacheColorHint" -> list.cacheColorHint = parseColor(value, context) - "choiceMode" -> list.choiceMode = parseChoiceMode(value) - "drawSelectorOnTop" -> list.isDrawSelectorOnTop = parseBoolean(value) - "fastScrollEnabled" -> list.isFastScrollEnabled = parseBoolean(value) - "listSelector" -> list.selector = parseDrawable(value, context) - "smoothScrollbar" -> list.isSmoothScrollbarEnabled = parseBoolean(value) - "stackFromBottom" -> list.isStackFromBottom = parseBoolean(value) - "textFilterEnabled" -> list.isTextFilterEnabled = parseBoolean(value) - "transcriptMode" -> list.transcriptMode = parseTranscriptMode(value) - else -> handled = false - } - - if (!handled) { - handled = super.apply(attribute, view) - } - - return handled - } - - protected open fun parseTranscriptMode(value: String): Int { - return when (value) { - "normal" -> AbsListView.TRANSCRIPT_MODE_NORMAL - "alwaysScroll" -> AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL - else -> AbsListView.TRANSCRIPT_MODE_DISABLED - } - } - - protected open fun parseChoiceMode(value: String): Int { - return when (value) { - "multipleChoice" -> AbsListView.CHOICE_MODE_MULTIPLE - "multipleChoiceModal" -> AbsListView.CHOICE_MODE_MULTIPLE_MODAL - "singleChoice" -> AbsListView.CHOICE_MODE_SINGLE - else -> AbsListView.CHOICE_MODE_NONE - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java deleted file mode 100644 index d733aeb98..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.AbsSeekBar; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for handling attributes related to AbsSeekBar. - * - * @author Akash Yadav - */ -public abstract class AbsSeekBarAttrAdapter extends ProgressBarAttrAdapter { - - public AbsSeekBarAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof AbsSeekBar; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var seek = (AbsSeekBar) view; - final var namespace = attribute.getNamespace(); - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(namespace)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "thumbTint": - seek.setThumbTintList(parseColorStateList(value, seek.getContext())); - break; - case "thumbTintMode": - seek.setThumbTintMode(parsePorterDuffMode(value)); - break; - case "tickMarkTint": - seek.setTickMarkTintList(parseColorStateList(value, seek.getContext())); - break; - case "tickMarkTintMode": - seek.setTickMarkTintMode(parsePorterDuffMode(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java deleted file mode 100644 index 2eb4f9ac1..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.AbsSpinner; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -public class AbsSpinnerAttrAdapter extends AdapterViewAttrAdapter { - - public AbsSpinnerAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof AbsSpinner; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var spinner = (AbsSpinner) view; - final var context = spinner.getContext(); - final var namespace = attribute.getNamespace(); - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - boolean handled = true; - - if (!canHandleNamespace(namespace)) { - return false; - } - - switch (name) { - case "entries": - final var array = parseArray(value); - final var adapter = newSimpleAdapter(context, array); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(adapter); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java deleted file mode 100644 index 133b25380..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.content.Context; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; - -/** - * Adapter for handling attributes related to AdapterView. - * - * @author Akash Yadav - */ -public abstract class AdapterViewAttrAdapter extends ViewGroupAttrAdapter { - - public AdapterViewAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof AdapterView; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - // No special attributes for AdapterView - return super.apply(attribute, view); - } - - protected ArrayAdapter newSimpleAdapter(Context ctx) { - return newSimpleAdapter(ctx, newAdapterItems(4)); - } - - protected ArrayAdapter newSimpleAdapter(Context ctx, String[] items) { - return new ArrayAdapter(ctx, android.R.layout.simple_list_item_1, items); - } - - protected String[] newAdapterItems(int size) { - final var items = new String[size]; - for (var i = 0; i < size; i++) { - items[i] = "Item " + i; - } - return items; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java deleted file mode 100644 index 484e7aafc..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.Button; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Adapter for handling attributes related to Button. - * - *

However, {@code android.widget.Button} has no special attributes. This adpater simply inherits - * from {@link TextViewAttrAdapter}. - * - * @author Akash Yadav - */ -public class ButtonAttrAdapter extends TextViewAttrAdapter { - - public ButtonAttrAdapter(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof Button; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - // No special attributes for Button - return super.apply(attribute, view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java deleted file mode 100644 index fc59cb8fd..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.CheckBox; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** Attribute handler for handling attributes related to CheckBox */ -public class CheckBoxAttrAdapter extends CompondButtonAttrAdapter { - - public CheckBoxAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof CheckBox; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - - // No special attributes for CheckBox - return super.apply(attribute, view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java deleted file mode 100644 index 30acd6973..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.CheckedTextView; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute handler for handling attributes related to CheckedTextView. - * - * @author Akash Yadav - */ -public class CheckedTextViewAttrAdapter extends TextViewAttrAdapter { - - public CheckedTextViewAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof CheckedTextView; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - - final CheckedTextView text = (CheckedTextView) view; - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "checkMarkTintMode": - text.setCheckMarkTintMode(parsePorterDuffMode(value)); - break; - case "checkMarkTint": - text.setCheckMarkTintList(parseColorStateList(value, text.getContext())); - break; - case "checkMark": - // Ignored... - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java deleted file mode 100644 index 7dc02e551..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.itsaky.inflater.adapters.android.widget; - -import android.content.Context; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.CompoundButton; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute handler for handling attibutes to CompoundButton - * - * @author Akash Yadav - */ -public class CompondButtonAttrAdapter extends ButtonAttrAdapter { - - public CompondButtonAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof CompoundButton; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final CompoundButton button = (CompoundButton) view; - final Context context = button.getContext(); - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "buttonTint": - button.setButtonTintList(parseColorStateList(value, context)); - break; - case "button": - button.setButtonDrawable(parseDrawable(value, context)); - break; - case "buttonTintMode": - button.setButtonTintMode(parsePorterDuffMode(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java deleted file mode 100644 index 029d170bb..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.EditText; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -public class EditTextAttrAdapter extends TextViewAttrAdapter { - - public EditTextAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof EditText; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - // No special attributes for EditText - return super.apply(attribute, view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/FrameLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/FrameLayoutAttrAdapter.java deleted file mode 100644 index 4d5180da3..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/FrameLayoutAttrAdapter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; - -/** - * Attribute adapter for {@link android.widget.FrameLayout} - * - * @author Akash Yadav - */ -public class FrameLayoutAttrAdapter extends ViewGroupAttrAdapter { - - public FrameLayoutAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof FrameLayout; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - - final var frame = (FrameLayout) view; - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "foregroundGravity": - frame.setForegroundGravity(parseGravity(value)); - break; - case "measureAllChildren": - frame.setMeasureAllChildren(parseBoolean(value)); - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/GridLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/GridLayoutAttrAdapter.java deleted file mode 100644 index e5319ce7f..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/GridLayoutAttrAdapter.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.GridLayout; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; - -/** - * @author Akash Yadav - */ -public class GridLayoutAttrAdapter extends ViewGroupAttrAdapter { - - public GridLayoutAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof GridLayout; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var grid = (GridLayout) view; - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "alignmentMode": - grid.setAlignmentMode(parseAlignmentMode(value)); - break; - case "columnCount": - grid.setColumnCount(parseInteger(value, Integer.MIN_VALUE)); - break; - case "columnOrderPreserved": - grid.setColumnOrderPreserved(parseBoolean(value)); - break; - case "orientation": - grid.setOrientation(parseOrientation(value)); - break; - case "rowCount": - grid.setRowCount(parseInteger(value, Integer.MIN_VALUE)); - break; - case "rowOrderPreserved": - grid.setRowOrderPreserved(parseBoolean(value)); - break; - case "useDefaultMargins": - grid.setUseDefaultMargins(parseBoolean(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - protected int parseOrientation(String value) { - if ("vertical".equals(value)) { - return GridLayout.VERTICAL; - } - - return GridLayout.HORIZONTAL; - } - - protected int parseAlignmentMode(String value) { - if ("alignBounds".equals(value)) { - return GridLayout.ALIGN_BOUNDS; - } - - return GridLayout.ALIGN_MARGINS; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java deleted file mode 100644 index 6f3a0674f..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.ImageButton; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute handler for handling attributes related to ImageButton. - * - * @author Akash Yadav - */ -public class ImageButtonAttrAdapter extends ImageViewAttrAdapter { - - public ImageButtonAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof ImageButton; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - // No special attributes for ImageButton - return super.apply(attribute, view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java deleted file mode 100644 index 094058986..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.ImageView; - -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter; - -/** - * Attribute handler for handling attributes related to ImageButton. - * - * @author Akash Yadav - */ -public class ImageViewAttrAdapter extends ViewAttrAdapter { - - public ImageViewAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof ImageView; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final ImageView image = (ImageView) view; - final Context context = image.getContext(); - final DisplayMetrics dm = context.getResources().getDisplayMetrics(); - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "adjustViewBounds": - image.setAdjustViewBounds(parseBoolean(value)); - break; - case "baseline": - image.setBaseline(parseDimension(value, 0, dm)); - break; - case "baselineAlignBottom": - image.setBaselineAlignBottom(parseBoolean(value)); - break; - case "cropToPadding": - image.setCropToPadding(parseBoolean(value)); - break; - case "maxHeight": - image.setMaxHeight(parseDimension(value, 0, dm)); - break; - case "maxWidth": - image.setMaxWidth(parseDimension(value, 0, dm)); - break; - case "scaleType": - image.setScaleType(parseScaleType(value)); - break; - case "src": - image.setImageDrawable(parseImageResource(value, context)); - break; - case "tint": - image.setImageTintList(parseColorStateList(value, context)); - break; - case "tintMode": - image.setImageTintMode(parsePorterDuffMode(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - private Drawable parseImageResource(String value, Context context) { - if (value.startsWith("@drawable/") || value.startsWith("@mipmap/")) { - return parseDrawable(value, context); - } - return ContextCompat.getDrawable(context, android.R.drawable.ic_delete); - } - - private ImageView.ScaleType parseScaleType(String value) { - switch (value) { - case "center": - return ImageView.ScaleType.CENTER; - case "centerCrop": - return ImageView.ScaleType.CENTER_CROP; - case "centerInside": - return ImageView.ScaleType.CENTER_INSIDE; - case "fitEnd": - return ImageView.ScaleType.FIT_END; - case "fitStart": - return ImageView.ScaleType.FIT_START; - case "fitXY": - return ImageView.ScaleType.FIT_XY; - case "matrix": - return ImageView.ScaleType.MATRIX; - case "fitCenter": - default: - return ImageView.ScaleType.FIT_CENTER; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java deleted file mode 100644 index 169e31e78..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.LinearLayout; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; - -/** - * Adapter for handling attributes related to LinearLayout. - * - * @author Akash Yadav - */ -public class LinearLayoutAttrAdapter extends ViewGroupAttrAdapter { - - public LinearLayoutAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof LinearLayout; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final LinearLayout linear = (LinearLayout) view; - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "baselineAligned": - linear.setBaselineAligned(parseBoolean(value)); - break; - case "baselineAlignedChildIndex": - linear.setBaselineAlignedChildIndex(parseInteger(value, linear.getChildCount())); - break; - case "gravity": - linear.setGravity(parseGravity(value)); - break; - case "measureWithLargestChild": - linear.setMeasureWithLargestChildEnabled(parseBoolean(value)); - break; - case "orientation": - linear.setOrientation(parseOrientation(value)); - break; - case "weightSum": - linear.setWeightSum(parseFloat(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - protected int parseOrientation(@NonNull String value) { - switch (value) { - case "vertical": - return LinearLayout.VERTICAL; - case "horizontal": - default: - return LinearLayout.HORIZONTAL; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ListViewAttrAdapter.kt b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ListViewAttrAdapter.kt deleted file mode 100644 index bab935898..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ListViewAttrAdapter.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget - -import android.util.DisplayMetrics -import android.view.View -import android.widget.ArrayAdapter -import android.widget.ListView -import com.blankj.utilcode.util.SizeUtils -import com.itsaky.inflater.IAttribute -import com.itsaky.inflater.IResourceTable - -/** @author Akash Yadav */ -class ListViewAttrAdapter(resourceTable: IResourceTable, displayMetrics: DisplayMetrics) : - AbsListViewAttrAdapter(resourceTable, displayMetrics) { - - override fun isApplicableTo(view: View?): Boolean { - return view is ListView - } - - override fun apply(attribute: IAttribute, view: View): Boolean { - val list = view as ListView - val value = attribute.value - val context = list.context - - if (!canHandleNamespace(attribute)) { - return false - } - - var handled = true - - when (attribute.attributeName) { - "divider" -> list.divider = parseDrawable(value, context) - "dividerHeight" -> - list.dividerHeight = parseDimension(value, SizeUtils.dp2px(1f), displayMetrics) - "entries" -> { - val entries = parseArray(value) - list.adapter = ArrayAdapter(context, android.R.layout.simple_list_item_1, entries) - } - "footerDividersEnabled" -> list.setFooterDividersEnabled(parseBoolean(value)) - "headerDividersEnabled" -> list.setHeaderDividersEnabled(parseBoolean(value)) - } - - if (!handled) { - handled = super.apply(attribute, view) - } - - return handled - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java deleted file mode 100644 index 77cadccb9..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.content.Context; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.ProgressBar; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter; - -/** - * Attribute adapter for handling attributes related to ProgressBar. - * - * @author Akash Yadav - */ -public class ProgressBarAttrAdapter extends ViewAttrAdapter { - - public ProgressBarAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof ProgressBar; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final ProgressBar pb = (ProgressBar) view; - final Context context = pb.getContext(); - final DisplayMetrics dm = context.getResources().getDisplayMetrics(); - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "indeterminate": - pb.setIndeterminate(parseBoolean(value)); - break; - case "indeterminateDrawable": - pb.setIndeterminateDrawable(parseDrawable(value, context)); - break; - case "indeterminateTint": - pb.setIndeterminateTintList(parseColorStateList(value, context)); - break; - case "indeterminateTintMode": - pb.setIndeterminateTintMode(parsePorterDuffMode(value)); - break; - case "max": - pb.setMax(parseInteger(value, 100)); - break; - case "maxHeight": - if (isApi29()) { - pb.setMaxHeight(parseDimension(value, Integer.MAX_VALUE, dm)); - } - break; - case "maxWidth": - if (isApi29()) { - pb.setMaxWidth(parseDimension(value, Integer.MAX_VALUE, dm)); - } - break; - case "min": - pb.setMin(parseInteger(value, 0)); - break; - case "minHeight": - if (isApi29()) { - pb.setMinHeight(parseDimension(value, 0, dm)); - } - break; - case "minWidth": - if (isApi29()) { - pb.setMinWidth(parseDimension(value, 0, dm)); - } - break; - case "progress": - pb.setProgress(parseInteger(value, 50)); - break; - case "progressBackgroundTint": - pb.setProgressBackgroundTintList(parseColorStateList(value, context)); - break; - case "progressBackgroundTintMode": - pb.setProgressBackgroundTintMode(parsePorterDuffMode(value)); - break; - case "progressDrawable": - pb.setProgressDrawable(parseDrawable(value, context)); - break; - case "progressTint": - pb.setProgressTintList(parseColorStateList(value, context)); - break; - case "progressTintMode": - pb.setProgressTintMode(parsePorterDuffMode(value)); - break; - case "secondaryProgress": - pb.setSecondaryProgress(parseInteger(value, 0)); - break; - case "secondaryProgressTint": - pb.setSecondaryProgressTintList(parseColorStateList(value, context)); - break; - case "secondaryProgressTintMode": - pb.setSecondaryProgressTintMode(parsePorterDuffMode(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java deleted file mode 100644 index fdcafca02..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.RadioButton; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for handling attributes related to RadioButton. - * - * @author Akash Yadav - */ -public class RadioButtonAttrAdapter extends CompondButtonAttrAdapter { - - public RadioButtonAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof RadioButton; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - // No special attributes for RadioButton - return super.apply(attribute, view); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java deleted file mode 100644 index c26ed3523..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.RelativeLayout; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter; - -/** - * Attribute adapter for handling attributes related to RelativeLayout. - * - * @author Akash Yadav - */ -public class RelativeLayoutAttrAdapter extends ViewGroupAttrAdapter { - - public RelativeLayoutAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof RelativeLayout; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - - final RelativeLayout relative = (RelativeLayout) view; - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "gravity": - relative.setGravity(parseGravity(value)); - break; - case "ignoreGravity": - relative.setIgnoreGravity(parseId(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java deleted file mode 100644 index 3390dd649..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.SeekBar; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for handling attributes related to SeekBar. - * - * @author Akash Yadav - */ -public class SeekBarAttrAdapter extends AbsSeekBarAttrAdapter { - - public SeekBarAttrAdapter(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof SeekBar; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var seek = (SeekBar) view; - final var context = seek.getContext(); - final var namespace = attribute.getNamespace(); - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(namespace)) { - return false; - } - boolean handled = true; - - switch (name) { - case "thumb": - seek.setThumb(parseDrawable(value, context)); - break; - default: - handled = false; - break; - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java deleted file mode 100644 index bbb21a555..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.Spinner; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for handling attributes related to Spinner. - * - * @author Akash Yadav - */ -public class SpinnerAttrAdapter extends AbsSpinnerAttrAdapter { - - public SpinnerAttrAdapter(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof Spinner; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var spinner = (Spinner) view; - final var context = spinner.getContext(); - final var dm = context.getResources().getDisplayMetrics(); - final var namespace = attribute.getNamespace(); - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(namespace)) { - return false; - } - - boolean handled = true; - - switch (name) { - case "dropDownHorizontalOffset": - spinner.setDropDownHorizontalOffset(parseDimension(value, 0, dm)); - break; - case "dropDownVerticalOffset": - spinner.setDropDownVerticalOffset(parseDimension(value, 0, dm)); - break; - case "dropDownWidth": - spinner.setDropDownWidth(parseDimension(value, 0, dm)); - break; - case "gravity": - spinner.setGravity(parseGravity(value)); - break; - case "popupBackground": - spinner.setPopupBackgroundDrawable(parseDrawable(value, context)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SwitchAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SwitchAttrAdapter.java deleted file mode 100644 index 0dea4765a..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SwitchAttrAdapter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.annotation.SuppressLint; -import android.graphics.Typeface; -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.Switch; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for {@link Switch}. - * - * @author Akash Yadav - */ -public class SwitchAttrAdapter extends CompondButtonAttrAdapter { - - public SwitchAttrAdapter(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof Switch; - } - - @SuppressLint("UseSwitchCompatOrMaterialCode") - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var button = (Switch) view; - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "showText": - button.setShowText(parseBoolean(value)); - break; - case "splitTrack": - button.setSplitTrack(parseBoolean(value)); - break; - case "switchMinWidth": - button.setSwitchMinWidth(parseDimension(value, 0)); - break; - case "switchPadding": - button.setSwitchPadding(parseDimension(value, 0)); - break; - case "textOff": - button.setTextOff(parseString(value)); - break; - case "textOn": - button.setTextOn(parseString(value)); - break; - case "textStyle": - button.setSwitchTypeface(Typeface.DEFAULT, parseTextStyle(value)); - break; - case "thumb": - button.setThumbDrawable(parseDrawable(value, button.getContext())); - break; - case "thumbTextPadding": - button.setThumbTextPadding(parseDimension(value, 0)); - break; - case "thumbTint": - button.setThumbTintList(parseColorStateList(value, button.getContext())); - break; - case "thumbTintMode": - button.setThumbTintMode(parsePorterDuffMode(value)); - break; - case "track": - button.setTrackDrawable(parseDrawable(value, button.getContext())); - break; - case "trackTint": - button.setTrackTintList(parseColorStateList(value, button.getContext())); - break; - case "trackTintMode": - button.setTrackTintMode(parsePorterDuffMode(value)); - break; - case "typeface": - button.setSwitchTypeface(parseTypeface(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java deleted file mode 100644 index f6520b6ad..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.text.util.Linkify; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.SizeUtils; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter; - -import java.util.regex.Pattern; - -/** - * Adapter for handling attributes related to TextView. - * - * @author Akash Yadav - */ -public class TextViewAttrAdapter extends ViewAttrAdapter { - - public TextViewAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof TextView; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final TextView text = (TextView) view; - final String name = attribute.getAttributeName(); - final String value = attribute.getValue(); - final Resources res = view.getResources(); - final DisplayMetrics dm = res.getDisplayMetrics(); - final Context ctx = view.getContext(); - final Drawable[] drawables = text.getCompoundDrawables(); - final Drawable[] drawablesRelative = text.getCompoundDrawablesRelative(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "autoLink": - text.setAutoLinkMask(parseAutoLinkMask(value)); - break; - case "drawableLeft": - text.setCompoundDrawables( - parseDrawable(value, ctx), drawables[1], drawables[2], drawables[3]); - break; - case "drawableTop": - text.setCompoundDrawables( - drawables[0], parseDrawable(value, ctx), drawables[2], drawables[3]); - break; - case "drawableRight": - text.setCompoundDrawables( - drawables[0], drawables[1], parseDrawable(value, ctx), drawables[3]); - break; - case "drawableBottom": - text.setCompoundDrawables( - drawables[0], drawables[1], drawables[2], parseDrawable(value, ctx)); - break; - case "drawableStart": - text.setCompoundDrawables( - parseDrawable(value, ctx), - drawablesRelative[1], - drawablesRelative[2], - drawablesRelative[3]); - break; - case "drawableEnd": - text.setCompoundDrawables( - drawablesRelative[0], - drawablesRelative[1], - parseDrawable(value, ctx), - drawablesRelative[3]); - break; - case "drawablePadding": - text.setCompoundDrawablePadding(parseDimension(value, 0, dm)); - break; - case "ellipsize": - text.setEllipsize(parseEllipsize(value)); - break; - case "gravity": - text.setGravity(parseGravity(value)); - break; - case "hint": - text.setHint(parseString(value)); - break; - case "letterSpacing": - text.setLetterSpacing(parseFloat(value)); - break; - case "lineHeight": - text.setLines(parseInteger(value, Integer.MAX_VALUE)); - break; - case "linksClickable": - text.setLinksClickable(parseBoolean(value)); - break; - case "marqueeRepeatLimit": - text.setMarqueeRepeatLimit(parseInteger(value, Integer.MAX_VALUE)); - break; - case "maxLines": - text.setMaxLines(parseInteger(value, Integer.MAX_VALUE)); - break; - case "minLines": - text.setMinLines(parseInteger(value, 1)); - break; - case "singleLine": - text.setSingleLine(parseBoolean(value)); - break; - case "text": - text.setText(parseString(value)); - break; - case "textAllCaps": - text.setAllCaps(parseBoolean(value)); - break; - case "textColor": - text.setTextColor(parseColor(value, ctx)); - break; - case "textColorHint": - text.setHintTextColor(parseColor(value, ctx)); - break; - case "textSize": - text.setTextSize( - TypedValue.COMPLEX_UNIT_PX, parseDimension(value, SizeUtils.sp2px(14), dm)); - break; - case "textStyle": - text.setTypeface(text.getTypeface(), parseTextStyle(value)); - break; - case "typeface": - text.setTypeface(parseTypeface(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } - - protected int parseTextStyle(@NonNull String value) { - final String[] splits = value.split(Pattern.quote("|")); - int mask = 0; - for (String split : splits) { - mask |= textStyleFor(split); - } - return mask; - } - - protected int textStyleFor(@NonNull String split) { - switch (split) { - case "bold": - return Typeface.BOLD; - case "italic": - return Typeface.ITALIC; - case "normal": - default: - return Typeface.NORMAL; - } - } - - protected Typeface parseTypeface(@NonNull String value) { - switch (value) { - case "sans": - return Typeface.SANS_SERIF; - case "serif": - return Typeface.SERIF; - case "monospace": - return Typeface.MONOSPACE; - case "normal": - default: - return Typeface.DEFAULT; - } - } - - protected TextUtils.TruncateAt parseEllipsize(@NonNull String value) { - switch (value) { - case "end": - return TextUtils.TruncateAt.END; - case "start": - return TextUtils.TruncateAt.START; - case "marquee": - return TextUtils.TruncateAt.MARQUEE; - case "middle": - return TextUtils.TruncateAt.MIDDLE; - case "none": - default: - return null; - } - } - - protected int parseAutoLinkMask(@NonNull String value) { - final String[] splits = value.split(Pattern.quote("|")); - int mask = 0; - for (String split : splits) { - mask |= autoLinkMaskFor(split); - } - return mask; - } - - protected int autoLinkMaskFor(@NonNull String mask) { - switch (mask) { - case "all": - return Linkify.ALL; - case "web": - return Linkify.WEB_URLS; - case "phone": - return Linkify.PHONE_NUMBERS; - case "map": - return Linkify.MAP_ADDRESSES; - case "email": - return Linkify.EMAIL_ADDRESSES; - case "none": - default: - return 0; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ToggleButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ToggleButtonAttrAdapter.java deleted file mode 100644 index 65c21a361..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ToggleButtonAttrAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.adapters.android.widget; - -import android.util.DisplayMetrics; -import android.view.View; -import android.widget.ToggleButton; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.ReflectUtils; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IResourceTable; - -/** - * Attribute adapter for {@link ToggleButton}. - * - * @author Akash Yadav - */ -public class ToggleButtonAttrAdapter extends CompondButtonAttrAdapter { - - public ToggleButtonAttrAdapter( - @NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - super(resourceFinder, displayMetrics); - } - - @Override - public boolean isApplicableTo(View view) { - return view instanceof ToggleButton; - } - - @Override - public boolean apply(@NonNull IAttribute attribute, @NonNull View view) { - final var button = (ToggleButton) view; - final var name = attribute.getAttributeName(); - final var value = attribute.getValue(); - - if (!canHandleNamespace(attribute)) { - return false; - } - - boolean handled = true; - switch (name) { - case "disabledAlpha": - ReflectUtils.reflect(button).field("mDisabledAlpha", parseFloat(value, 0.5f)); - break; - case "textOff": - button.setTextOff(parseString(value)); - break; - case "textOn": - button.setTextOn(parseString(value)); - break; - default: - handled = false; - break; - } - - if (!handled) { - handled = super.apply(attribute, view); - } - - return handled; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/BitmapDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/BitmapDrawableParser.java deleted file mode 100644 index 79ddd4985..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/BitmapDrawableParser.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Shader; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.InflateException; - -import org.jetbrains.annotations.Contract; -import org.xmlpull.v1.XmlPullParser; - -/** - * Parser for parsing <bitmap> drawables. - * - * @author Akash Yadav - */ -public class BitmapDrawableParser extends IDrawableParser { - - protected BitmapDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - var index = attrIndex("src"); - if (index == -1) { - throw new InflateException("Invalid drawable. No android:src specified!"); - } - - var val = value(index); - var dr = parseDrawable(val, BaseApplication.getBaseInstance()); - if (dr == null) { - throw new InflateException("Cannot parse drawable for android:src = " + val); - } - - final var bitmap = - new BitmapDrawable(BaseApplication.getBaseInstance().getResources(), toBitmap(dr)); - - index = attrIndex("antialias"); - if (index != -1) { - bitmap.setAntiAlias(parseBoolean(value(index))); - } - - index = attrIndex("dither"); - if (index != -1) { - bitmap.setDither(parseBoolean(value(index))); - } - - index = attrIndex("filter"); - if (index != -1) { - bitmap.setFilterBitmap(parseBoolean(value(index))); - } - - index = attrIndex("gravity"); - if (index != -1) { - bitmap.setGravity(parseGravity(value(index))); - } - - index = attrIndex("mipMap"); - if (index != -1) { - bitmap.setMipMap(parseBoolean(value(index))); - } - - index = attrIndex("tileMode"); - if (index != -1) { - var mode = parseTileMode(value(index)); - bitmap.setTileModeXY(mode, mode); - } - - index = attrIndex("tileModeX"); - if (index != -1) { - var mode = parseTileMode(value(index)); - bitmap.setTileModeX(mode); - } - - index = attrIndex("tileModeY"); - if (index != -1) { - var mode = parseTileMode(value(index)); - bitmap.setTileModeY(mode); - } - - return bitmap; - } - - @Nullable - @Contract(pure = true) - private Shader.TileMode parseTileMode(@NonNull String value) { - switch (value) { - case "clamp": - return Shader.TileMode.CLAMP; - case "mirror": - return Shader.TileMode.MIRROR; - case "repeat": - return Shader.TileMode.REPEAT; - case "disabled": - default: - return null; // null = disabled - } - } - - private Bitmap toBitmap(@NonNull Drawable dr) { - if (dr instanceof BitmapDrawable) { - var bit = (BitmapDrawable) dr; - if (bit.getBitmap() != null) { - return bit.getBitmap(); - } - } - - Bitmap bit; - if (dr.getIntrinsicWidth() > 0 && dr.getIntrinsicHeight() > 0) { - bit = - Bitmap.createBitmap( - dr.getIntrinsicWidth(), dr.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - } else { - bit = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } - - final var canvas = new Canvas(bit); - dr.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - dr.draw(canvas); - return bit; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ClipDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ClipDrawableParser.java deleted file mode 100644 index 97e6ac26a..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ClipDrawableParser.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.graphics.drawable.ClipDrawable; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.view.Gravity; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.InflateException; -import com.itsaky.inflater.util.Preconditions; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Parser for parsing <clip> drawables; - * - * @author Akash Yadav - */ -public class ClipDrawableParser extends IDrawableParser { - - protected ClipDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - var index = attrIndex("drawable"); - if (index == -1) { - throw new InflateException(" drawable must specify android:drawable attribute"); - } - - var val = value(index); - Preconditions.assertNotBlank(val, "Invalid value specified to android:drawable attribute"); - - final var drawable = parseDrawable(val, BaseApplication.getBaseInstance()); - if (drawable == null) { - throw new InflateException("Unable to parse drawable for value: " + val); - } - - var orientation = ClipDrawable.HORIZONTAL; - index = attrIndex("clipOrientation"); - if (index != -1) { - orientation = parseClipOrientation(value(index)); - } - - var gravity = Gravity.LEFT; - index = attrIndex("gravity"); - if (index != -1) { - gravity = parseGravity(value(index)); - } - - return new ClipDrawable(drawable, gravity, orientation); - } - - protected int parseClipOrientation(@NonNull final String value) { - switch (value) { - case "vertical": - return ClipDrawable.VERTICAL; - default: - case "horizontal": - return ClipDrawable.HORIZONTAL; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java deleted file mode 100644 index 75e9c68ee..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.content.Context; -import android.graphics.NinePatch; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.NinePatchDrawable; -import android.util.DisplayMetrics; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.blankj.utilcode.util.FileIOUtils; -import com.blankj.utilcode.util.ImageUtils; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IResourceTable; -import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -/** - * Utility methods for creating instances of suitable drawable parsers for a file or an XML code. - * - * @author Akash Yadav - */ -public abstract class DrawableParserFactory { - - private static final ILogger LOG = ILogger.newInstance("IDrawableParser"); - - /** - * Create a new drawable parser for the given file. If the given file is not an XML Document, then - * a no-op parser is returned which simply returns the drawable for that file. If the file is an - * XML document, then a suitable parser is determined by looking at the root tag of the drawable. - * - * @param context The context. This will be used for obtaining app resources. - * @param file The file to parse. - * @param resourceFinder The resource finder. Used for finding references to another resources. - * @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if - * there were any issues creating the parser. - * @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}. - * @throws IOException Thrown by {@link XmlPullParser#next()} - */ - @Nullable - public static IDrawableParser newParser( - @NonNull final Context context, @NonNull final File file, final IResourceTable resourceFinder) - throws XmlPullParserException, IOException { - if (file.getName().endsWith(".xml")) { - final var code = FileIOUtils.readFile2String(file); - return DrawableParserFactory.newXmlDrawableParser( - code, resourceFinder, context.getResources().getDisplayMetrics()); - } else { - final var bitmap = ImageUtils.getBitmap(file); - if (bitmap == null) { - return null; - } - - final var chunk = bitmap.getNinePatchChunk(); - if (NinePatch.isNinePatchChunk(chunk)) { - return new NoParser( - new NinePatchDrawable(context.getResources(), new NinePatch(bitmap, chunk))); - } - - return new NoParser(new BitmapDrawable(context.getResources(), bitmap)); - } - } - - /** - * Create a new drawable parser for the given xml code. - * - * @param xmlDrawable The XML code. This must be valid. - * @param resourceFinder The resource finder. Used for finding references to another resources. - * @param displayMetrics This will be used by generated parsers to parse dimension vlaues. - * @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if - * there were any issues creating the parser. - * @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}. - * @throws IOException Thrown by {@link XmlPullParser#next()} - */ - @Nullable - public static IDrawableParser newXmlDrawableParser( - String xmlDrawable, IResourceTable resourceFinder, DisplayMetrics displayMetrics) - throws XmlPullParserException, IOException { - final var factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - - final var parser = factory.newPullParser(); - parser.setInput(new StringReader(xmlDrawable)); - - var event = parser.getEventType(); - while (event != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final var name = parser.getName(); - return parserForTag(xmlDrawable, resourceFinder, displayMetrics, parser, name); - } - event = parser.next(); - } - - // TODO Implement parsers for these root tags if possible - // 1. ---------- DONE - // 2. ------ DONE - // 3. ------ DONE - // 4. - // 5. - // 6. - // 7. ----------- DONE - // 8. ------------ DONE - // 9. ----------- DONE - // Cannot parse this type of drawable - return null; - } - - @Nullable - public static IDrawableParser parserForTag( - String xmlDrawable, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - XmlPullParser parser, - @NonNull String name) - throws XmlPullParserException { - Class impl = null; - switch (name) { - case "shape": - impl = ShapeDrawableParser.class; - break; - case "inset": - impl = InsetDrawableParser.class; - break; - case "layer-list": - impl = LayerListParser.class; - break; - case "bitmap": - impl = BitmapDrawableParser.class; - break; - case "scale": - impl = ScaleDrawableParser.class; - break; - case "clip": - impl = ClipDrawableParser.class; - break; - case "selector": - impl = StateListParser.class; - break; - case "vector": - if (xmlDrawable != null) { - return new NoParser(VectorMasterDrawable.fromXML(xmlDrawable)); - } - return null; - } - - try { - if (impl != null) { - final var constructor = - impl.getDeclaredConstructor( - XmlPullParser.class, IResourceTable.class, DisplayMetrics.class, int.class); - return constructor.newInstance( - parser, resourceFinder, displayMetrics, IDrawableParser.ANY_DEPTH); - } - } catch (Throwable th) { - LOG.error("No drawable parser found for tag", name, th); - } - - return null; - } - - /** Instead of parsing anything, this returns the provided drawable. */ - private static class NoParser extends IDrawableParser { - - private final Drawable parsed; - - protected NoParser(final Drawable parsed) { - super(null, null, null, ANY_DEPTH); - this.parsed = parsed; - } - - @Override - public Drawable parse() throws Exception { - return parsed; - } - - @Override - public Drawable parseDrawable() { - return parsed; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java deleted file mode 100644 index 4ceb96386..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.drawable; - -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.DisplayMetrics; - -import androidx.annotation.ChecksSdkIntAtLeast; - -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.util.CommonParseUtils; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Base class for drawable parsers. - * - * @author Akash Yadav - */ -public abstract class IDrawableParser extends CommonParseUtils { - - public static final int ANY_DEPTH = -1; - - protected final XmlPullParser parser; - protected int minDepth; - - protected IDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(resourceFinder, displayMetrics); - this.parser = parser; - this.minDepth = minDepth; - } - - /** - * Parse the drawable using the already provided parser and data. - * - * @return The parsed {@link Drawable} or null if the parse was unsuccessful. - * @throws Exception If any fatal error occurred while parsing the drawable. - */ - public Drawable parse() throws Exception { - var index = attrIndex("visible"); - var visible = true; - if (index != -1) { - visible = parseBoolean(value(index)); - } - - var autoMirrored = false; - index = attrIndex("autoMirrored"); - if (index != -1) { - autoMirrored = parseBoolean(value(index)); - } - - var level = 0; - index = attrIndex("level"); - if (index != -1) { - level = parseInteger(value(index), 0); - } - - final var drawable = parseDrawable(); - - if (drawable != null) { - drawable.setVisible(visible, false); - drawable.setAutoMirrored(autoMirrored); - drawable.setLevel(level); - } - - return drawable; - } - - /** - * Actual implementation of the parse logic. - * - * @return The parsed drawable. Maybe null. - * @throws Exception If any fatal error occurs while parsing the drawable. - */ - protected abstract Drawable parseDrawable() throws Exception; - - /** - * Find the index of the attribute with the given name. - * - * @param name The name of the attribute to look for. - * @return The index of the attribute or -1. - */ - protected int attrIndex(final String name) { - for (int i = 0; i < this.parser.getAttributeCount(); i++) { - if (this.parser.getAttributeName(i).equals(name)) { - return i; - } - } - - return -1; - } - - /** - * Get the value of the attribute at the given index. - * - * @param index The index of the attribute. - * @return The value of the attribute. - */ - protected String value(int index) { - return parser.getAttributeValue(index); - } - - /** - * Set the minimum depth that this parser should keep parsing. Subclasses are expected to parse - * child tags with depths greater than this specified value. - * - * @param minDepth The minimum depth of this parser. - */ - public void setMinDepth(int minDepth) { - this.minDepth = minDepth; - } - - /** - * Checks if the drawable parser is allowed to parse at the current depth. This must be checked if - * the drawable parser keeps looking for {@link XmlPullParser#START_TAG} or {@link - * XmlPullParser#END_TAG}. - * - *

The {@link LayerListParser} can contain tags which are expected to be parsed by other - * parsers. So, if the nesting parser does not check if this method if true, it might - * consume all the events and the {@link LayerListParser} will have invalid data to parse. In some - * cases, the nested parser might consume all the events until {@link XmlPullParser#END_DOCUMENT}. - * - * @return true if the parser can keep parsing, false otherwise. - */ - protected boolean canParse() { - return this.parser.getDepth() >= minDepth; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) - protected boolean isApi28() { - return Build.VERSION.SDK_INT >= 28; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q) - protected boolean isApi29() { - return Build.VERSION.SDK_INT >= 29; - } - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) - protected boolean isApi30() { - return Build.VERSION.SDK_INT >= 30; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/InsetDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/InsetDrawableParser.java deleted file mode 100644 index 81872f702..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/InsetDrawableParser.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; -import android.util.DisplayMetrics; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.InflateException; -import com.itsaky.inflater.util.Preconditions; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Parser for <inset> drawables. - * - * @author Akash Yadav - */ -public class InsetDrawableParser extends IDrawableParser { - - protected InsetDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - var index = attrIndex("drawable"); - if (index == -1) { - throw new InflateException("No drawable specified for drawable"); - } - - var value = value(index); - Preconditions.assertNotBlank(value, "Invalid value specified for android:drawable"); - - final var drawable = parseDrawable(value, BaseApplication.getBaseInstance()); - if (drawable == null) { - throw new InflateException( - "Unable to parse inset drawable. Failed to parse android:drawable"); - } - - index = attrIndex("insetLeft"); - final var left = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("insetTop"); - final var top = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("insetRight"); - final var right = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("insetBottom"); - final var bottom = index == -1 ? 0 : parseDimension(value(index), 0); - - return new InsetDrawable(drawable, left, top, right, bottom); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/LayerListParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/LayerListParser.java deleted file mode 100644 index bba53a68c..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/LayerListParser.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.drawable; - -import static com.itsaky.inflater.util.Preconditions.assertNotBlank; - -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.util.DisplayMetrics; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.util.TextDrawable; - -import org.jetbrains.annotations.Contract; -import org.xmlpull.v1.XmlPullParser; - -/** - * Parser for parsing <layer-list> drawables. - * - * @author Akash Yadav - */ -public class LayerListParser extends IDrawableParser { - - protected LayerListParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - final var layer = new LayerDrawable(new Drawable[0]); - var index = -1; - var value = ""; - var event = parser.getEventType(); - int left = 0, top = 0, right = 0, bottom = 0; - var parseInner = true; - while (event != XmlPullParser.END_DOCUMENT && canParse()) { - if (event == XmlPullParser.START_TAG) { - final var name = parser.getName(); - if ("item".equals(name)) { - index = attrIndex("drawable"); - Drawable drawable = null; - if (index != -1) { - value = value(index); - assertNotBlank(value, "Invalid value for android:drawable attribute"); - drawable = parseDrawable(value, BaseApplication.getBaseInstance()); - - // if the android:drawable is specified, we do not parse the inner elements - // of - parseInner = false; - } - - index = attrIndex("left"); - left = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("top"); - top = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("right"); - right = index == -1 ? 0 : parseDimension(value(index), 0); - - index = attrIndex("bottom"); - bottom = index == -1 ? 0 : parseDimension(value(index), 0); - - if (drawable == null) { - parseInner = true; - } else { - addToLayer(layer, drawable, left, top, right, bottom); - } - - if (!parseInner) { - skipToEndTag(this.parser.getDepth()); - } - } else if (parser.getDepth() > 2 && parseInner) { - // depth 1 = - // depth 2 = - // depth > 2 = child drawables inside tag - final var parser = - DrawableParserFactory.parserForTag( - null, resourceFinder, displayMetrics, this.parser, name); - if (parser != null) { - parser.setMinDepth(this.parser.getDepth()); - var d = parser.parse(); - if (d == null) { - d = unsupported(); - } - - addToLayer(layer, d, left, top, right, bottom); - } else { - addToLayer(layer, unsupported(), left, top, right, bottom); - } - } - } - event = parser.next(); - } - - return layer; - } - - private void addToLayer( - @NonNull LayerDrawable layer, Drawable drawable, int left, int top, int right, int bottom) { - final int index = layer.addLayer(drawable); - layer.setLayerInset(index, left, top, right, bottom); - } - - private void skipToEndTag(int depth) throws Exception { - for (var event = parser.getEventType(); - event != XmlPullParser.END_DOCUMENT; - event = parser.next()) { - if (event == XmlPullParser.END_TAG && depth == parser.getDepth()) { - break; - } - } - } - - @NonNull - @Contract(" -> new") - private Drawable unsupported() { - return new TextDrawable("Unsupported drawable", displayMetrics); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ScaleDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ScaleDrawableParser.java deleted file mode 100644 index 994e6f498..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ScaleDrawableParser.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.graphics.drawable.Drawable; -import android.graphics.drawable.ScaleDrawable; -import android.util.DisplayMetrics; -import android.view.Gravity; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.InflateException; -import com.itsaky.inflater.util.Preconditions; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Parser for parsing <scale> drawables. - * - * @author Akash Yadav - */ -public class ScaleDrawableParser extends IDrawableParser { - - protected ScaleDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - var index = attrIndex("drawable"); - if (index == -1) { - throw new InflateException(" drawable must specify android:drawable attribute"); - } - var v = value(index); - Preconditions.assertNotBlank(v, "Invalid value specified for android:drawable attribute"); - - final var drawable = parseDrawable(v, BaseApplication.getBaseInstance()); - if (drawable == null) { - throw new InflateException("Unable to parse drawable: " + v); - } - - var gravity = Gravity.LEFT; - index = attrIndex("scaleGravity"); - if (index != -1) { - try { - gravity = parseGravity(value(index)); - } catch (Throwable th) { - // ignored - } - } - - var scaleWidth = -1.0f; - var scaleHeight = -1.0f; // DO_NOT_SCALE by default - - index = attrIndex("scaleWidth"); - if (index != -1) { - try { - scaleWidth = parseScale(value(index)); - } catch (Throwable th) { - // ignored - } - } - - index = attrIndex("scaleHeight"); - if (index != -1) { - try { - scaleHeight = parseScale(value(index)); - } catch (Throwable th) { - // ignored - } - } - - return new ScaleDrawable(drawable, gravity, scaleWidth, scaleHeight); - } - - private float parseScale(@NonNull String value) { - if (!value.endsWith("%")) { - throw new InflateException("Invalid scale value:" + value); - } - - final var factor = Float.parseFloat(value.substring(0, value.length() - 1)) / 100; - if (factor < 0f || factor > 1f) { - throw new InflateException("Scale factor must be between 0% and 100%"); - } - - return factor; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java deleted file mode 100644 index 6374c3854..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.util.DisplayMetrics; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.inflater.IResourceTable; - -import org.jetbrains.annotations.Contract; -import org.xmlpull.v1.XmlPullParser; - -import java.util.Arrays; - -/** - * Parses a drawable whose root element is {@code }. If the parse was successful, returns a - * {@link GradientDrawable}, otherwise {@code null}. - * - * @author Akash Yadav - */ -public class ShapeDrawableParser extends IDrawableParser { - - protected ShapeDrawableParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Nullable - @Override - public Drawable parseDrawable() throws Exception { - - final var drawable = new GradientDrawable(); - - var index = attrIndex("shape"); - drawable.setShape( - index == -1 - ? GradientDrawable.RECTANGLE - : parseShape(this.parser.getAttributeValue(index))); - - if (isApi29()) { - index = attrIndex("innerRadius"); - drawable.setInnerRadius(index == -1 ? 0 : parseDimension(value(index), 0)); - - index = attrIndex("innerRadiusRatio"); - drawable.setInnerRadiusRatio(index == -1 ? 1f : Float.parseFloat(value(index))); - - index = attrIndex("thickness"); - drawable.setThickness(index == -1 ? 0 : parseDimension(value(index), 0)); - - index = attrIndex("thicknessRatio"); - drawable.setThicknessRatio(index == -1 ? 1f : Float.parseFloat(value(index))); - } - - index = attrIndex("useLevel"); - drawable.setUseLevel(index != -1 && parseBoolean(value(index))); - - var event = parser.getEventType(); - while (event != XmlPullParser.END_DOCUMENT && canParse()) { - final var name = parser.getName(); - if (event == XmlPullParser.START_TAG) { - switch (name) { - case "corners": - parseCorners(drawable); - break; - case "gradient": - parseGradient(drawable); - break; - case "padding": - parsePadding(drawable); - break; - case "size": - parseSize(drawable); - break; - case "solid": - parseSolid(drawable); - break; - case "stroke": - parseStroke(drawable); - break; - } - } - event = parser.next(); - } - - return drawable; - } - - private void parseStroke(@NonNull final GradientDrawable drawable) { - var index = attrIndex("width"); - var strokeWidth = 0; - if (index != -1) { - drawable.setStroke(strokeWidth = parseDimension(value(index), 0), Color.TRANSPARENT); - } - - index = attrIndex("color"); - var strokeColor = Color.TRANSPARENT; - if (index != -1) { - drawable.setStroke(strokeWidth, strokeColor = parseColor(value(index), appContext())); - } - - index = attrIndex("dashWidth"); - var dashWidth = 0; - if (index != -1) { - drawable.setStroke(strokeWidth, strokeColor, dashWidth = parseDimension(value(index), 0), 0); - } - - index = attrIndex("dashGap"); - if (index != -1) { - drawable.setStroke(strokeWidth, strokeColor, dashWidth, parseDimension(value(index), 0)); - } - } - - private void parseSolid(@NonNull final GradientDrawable drawable) { - var index = attrIndex("color"); - if (index != -1) { - drawable.setColor(parseColor(value(index), appContext())); - } - } - - private void parseSize(@NonNull final GradientDrawable drawable) { - var index = attrIndex("width"); - if (index != -1) { - drawable.setSize(parseDimension(value(index), 0), drawable.getIntrinsicHeight()); - } - - index = attrIndex("height"); - if (index != -1) { - drawable.setSize(drawable.getIntrinsicWidth(), parseDimension(value(index), 0)); - } - } - - private void parsePadding(@NonNull final GradientDrawable drawable) { - - // Padding is available from API 29 only - if (!isApi29()) { - return; - } - - final var rect = new Rect(); - if (!drawable.getPadding(rect)) { - rect.set(0, 0, 0, 0); - } - - var changed = false; - var index = attrIndex("left"); - if (index != -1) { - rect.left = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("top"); - if (index != -1) { - rect.top = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("right"); - if (index != -1) { - rect.right = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("bottom"); - if (index != -1) { - rect.bottom = parseDimension(value(index), 0); - changed = true; - } - - if (changed) { - drawable.setPadding(rect.left, rect.top, rect.right, rect.bottom); - } - } - - private void parseGradient(@NonNull final GradientDrawable drawable) { - var index = attrIndex("angle"); - drawable.setOrientation( - index == -1 - ? GradientDrawable.Orientation.LEFT_RIGHT - : parseGradientOrientation(value(index))); - - index = attrIndex("centerX"); - var center = index == -1 ? drawable.getGradientCenterX() : Float.parseFloat(value(index)); - drawable.setGradientCenter(center, drawable.getGradientCenterY()); - - index = attrIndex("centerY"); - center = index == -1 ? drawable.getGradientCenterX() : Float.parseFloat(value(index)); - drawable.setGradientCenter(drawable.getGradientCenterX(), center); - - final var colors = new int[3]; - var changed = false; - Arrays.fill(colors, -1); - - index = attrIndex("centerColor"); - if (index != -1) { - colors[1] = parseColor(value(index), appContext()); - changed = true; - } - - index = attrIndex("endColor"); - if (index != -1) { - colors[2] = parseColor(value(index), appContext()); - changed = true; - } - - index = attrIndex("gradientRadius"); - drawable.setGradientRadius( - index == -1 ? drawable.getGradientRadius() : Float.parseFloat(value(index))); - - index = attrIndex("startColor"); - if (index != -1) { - colors[0] = parseColor(value(index), appContext()); - changed = true; - } - - if (changed) { - drawable.setColors(colors); - } - - index = attrIndex("type"); - drawable.setGradientType( - index == -1 ? GradientDrawable.LINEAR_GRADIENT : parseGradientType(value(index))); - - index = attrIndex("useLevel"); - drawable.setUseLevel(index != -1 && parseBoolean(value(index))); - } - - private int parseGradientType(String value) { - switch (value) { - case "radial": - return GradientDrawable.RADIAL_GRADIENT; - case "sweep": - return GradientDrawable.SWEEP_GRADIENT; - case "linear": - default: - return GradientDrawable.LINEAR_GRADIENT; - } - } - - private GradientDrawable.Orientation parseGradientOrientation(String value) { - final var angle = Integer.parseInt(value); - - // Angle must be between 0-315 and a multiple of 45 - // Angle moves in anti-clockwise direction - if (angle == 45) { - return GradientDrawable.Orientation.BL_TR; - } - - if (angle == 90) { - return GradientDrawable.Orientation.BOTTOM_TOP; - } - - if (angle == 135) { - return GradientDrawable.Orientation.BR_TL; - } - - if (angle == 180) { - return GradientDrawable.Orientation.RIGHT_LEFT; - } - - if (angle == 225) { - return GradientDrawable.Orientation.TR_BL; - } - - if (angle == 270) { - return GradientDrawable.Orientation.TOP_BOTTOM; - } - - if (angle == 315) { - return GradientDrawable.Orientation.TL_BR; - } - - // Angle 0 or any invalid valid should make the orientation left to right - return GradientDrawable.Orientation.LEFT_RIGHT; - } - - protected Context appContext() { - return BaseApplication.getBaseInstance(); - } - - private void parseCorners(@NonNull GradientDrawable drawable) { - int index; - index = attrIndex("radius"); - drawable.setCornerRadius(index == -1 ? 0f : parseDimension(value(index), 0)); - - var changed = false; - final var radii = new float[8]; - Arrays.fill(radii, 0f); - - index = attrIndex("topLeftRadius"); - if (index != -1) { - radii[0] = radii[1] = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("topRightRadius"); - if (index != -1) { - radii[2] = radii[3] = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("bottomLeftRadius"); - if (index != -1) { - radii[4] = radii[5] = parseDimension(value(index), 0); - changed = true; - } - - index = attrIndex("bottomRightRadius"); - if (index != -1) { - radii[6] = radii[7] = parseDimension(value(index), 0); - changed = true; - } - - if (changed) { - drawable.setCornerRadii(radii); - } - } - - @Contract(pure = true) - private int parseShape(@NonNull String value) { - switch (value) { - case "oval": - return GradientDrawable.OVAL; - case "line": - return GradientDrawable.LINE; - case "ring": - return GradientDrawable.RING; - case "rectangle": - default: - return GradientDrawable.RECTANGLE; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/StateListParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/StateListParser.java deleted file mode 100644 index c3b5494bd..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/StateListParser.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.drawable; - -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; -import android.util.DisplayMetrics; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.InflateException; - -import org.xmlpull.v1.XmlPullParser; - -import java.util.ArrayList; - -/** - * Parser for parsing <selector> drawables. - * - * @author Akash Yadav - */ -public class StateListParser extends IDrawableParser { - - private static final ILogger LOG = ILogger.newInstance("StateListParser"); - - protected StateListParser( - XmlPullParser parser, - IResourceTable resourceFinder, - DisplayMetrics displayMetrics, - int minDepth) { - super(parser, resourceFinder, displayMetrics, minDepth); - } - - @Override - public Drawable parseDrawable() throws Exception { - var states = new StateListDrawable(); - - // --------------------------- NOTE ------------------------- - // Unsupported attributes : - // 1. android:constantSize - // 2. android:variablePadding - - var index = attrIndex("dither"); - if (index != -1) { - states.setDither(parseBoolean(value(index))); - } - - var event = parser.getEventType(); - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - var name = parser.getName(); - if ("item".equals(name)) { - index = attrIndex("drawable"); - if (index == -1) { - throw new InflateException(" item does not define android:drawable"); - } - final var drawable = parseDrawable(value(index), BaseApplication.getBaseInstance()); - if (drawable == null) { - throw new InflateException("Unable to parse drawable for android:drawable attribute"); - } - - addStates(states, drawable); - } - } - } - - return states; - } - - /** - * Add all the defined states of the current tag in parser to the given state list - * drawable. - * - * @param states The drawable to add states to. - * @param drawable The drawable associated with the defined states; - */ - private void addStates(StateListDrawable states, Drawable drawable) { - final var stateList = new ArrayList(); - final var count = parser.getAttributeCount(); - for (int i = 0; i < count; i++) { - var name = parser.getAttributeName(i); - var value = parser.getAttributeValue(i); - if (!name.startsWith("state_")) { - continue; - } - - var state = reflectState(name); - - final var isEnabled = parseBoolean(value); - if (!isEnabled) { - // android:state_[state_name]="false" - state = -state; - } - - stateList.add(state); - } - - final var arr = new int[stateList.size()]; - for (int i = 0; i < stateList.size(); i++) { - arr[i] = stateList.get(i); - } - - states.addState(arr, drawable); - } - - private int reflectState(String name) { - try { - final var clazz = android.R.attr.class; - final var field = clazz.getDeclaredField(name); - return field.getInt(null); - } catch (Throwable th) { - LOG.error("Unable to get state ID with name:", name); - return -1; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java deleted file mode 100644 index efc83a58c..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseView.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater.impl; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IAttribute; -import com.itsaky.inflater.IAttributeAdapter; -import com.itsaky.inflater.IView; -import com.itsaky.inflater.IViewGroup; -import com.itsaky.xml.INamespace; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -public abstract class BaseView implements IView { - - public static final String XML_HEADER = ""; - - protected final Set namespaces = - new TreeSet<>(Comparator.comparing(INamespace::getPrefix)); - protected final List attributes = new ArrayList<>(); - protected final Set attrAdapters = new HashSet<>(); - - protected final String qualifiedName; - protected final View view; - protected final ILogger LOG = ILogger.newInstance(getClass().getSimpleName()); - protected IViewGroup parent; - protected Object stored; - private boolean isPlaceholder; - - public BaseView(String qualifiedName, View view) { - this(qualifiedName, view, false); - } - - public BaseView(String qualifiedName, View view, boolean isPlaceholder) { - this.qualifiedName = qualifiedName; - this.view = view; - this.isPlaceholder = isPlaceholder; - } - - @Override - public int hashCode() { - return Objects.hash( - attributes, attrAdapters, qualifiedName, view, getParent(), stored, isPlaceholder()); - } - - @Override - public void setParent(IViewGroup parent) { - this.parent = parent; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BaseView baseView = (BaseView) o; - return isPlaceholder() == baseView.isPlaceholder() - && Objects.equals(attributes, baseView.attributes) - && Objects.equals(attrAdapters, baseView.attrAdapters) - && Objects.equals(qualifiedName, baseView.qualifiedName) - && Objects.equals(view, baseView.view) - && Objects.equals(getParent(), baseView.getParent()) - && Objects.equals(stored, baseView.stored); - } - - public void setPlaceholder(boolean placeholder) { - this.isPlaceholder = placeholder; - } - - protected Set getAttributeAdapters() { - return attrAdapters; - } - - @Override - public boolean isPlaceholder() { - return isPlaceholder; - } - - @Override - public View asView() { - return view; - } - - @Override - public IViewGroup getParent() { - return parent; - } - - @Override - public boolean removeFromParent() { - if (getParent() != null) { - getParent().removeView(this); - return true; - } - - LOG.info("View does not have a parent. Cannot remove."); - return false; - } - - @Override - public void addAttribute(IAttribute attr) { - if (attr == null || this.attributes.contains(attr)) { - return; - } - - this.attributes.add(attr); - - applyAttributeValue(attr); - - this.attributes.sort(IAttribute.COMPARATOR); - } - - private void applyAttributeValue(final IAttribute attr) { - for (IAttributeAdapter adapter : attrAdapters) { - if (adapter.isApplicableTo(asView()) && adapter.apply(attr, asView())) { - break; - } - } - } - - @Override - public void removeAttribute(IAttribute attr) { - this.attributes.remove(attr); - } - - @Override - public void removeAttributeAt(int index) { - this.attributes.remove(index); - } - - @Override - public List getAttributes() { - return attributes; - } - - @Override - public IAttribute[] getAttrArray() { - return getAttributes().toArray(new IAttribute[0]); - } - - @Override - public boolean hasAttribute(INamespace namespace, String name) { - return this.attributes.stream() - .anyMatch( - attribute -> - Objects.equals(attribute.getNamespace(), namespace) - && attribute.getAttributeName().equals(name)); - } - - @Nullable - @Override - public IAttribute getAttribute(INamespace namespace, String name) { - for (var attr : this.attributes) { - if (Objects.equals(attr.getNamespace(), namespace) && attr.getAttributeName().equals(name)) { - return attr; - } - } - return null; - } - - @Override - public boolean updateAttribute(INamespace namespace, String name, String value) { - IAttribute found = null; - int index = -1; - for (int i = 0; i < attributes.size(); i++) { - final var attr = attributes.get(i); - if (Objects.equals(attr.getNamespace(), namespace) && attr.getAttributeName().equals(name)) { - found = attr; - index = i; - break; - } - } - - if (found != null && index < this.attributes.size()) { - found.apply(value); - this.attributes.set(index, found); - // Let the attribute adapters handle the update - applyAttributeValue(found); - - return true; - } - - return false; - } - - @Override - public void registerAttributeAdapter(IAttributeAdapter adapter) { - if (adapter == null) { - return; - } - - this.attrAdapters.add(adapter); - } - - @Override - public void setExtraData(Object data) { - this.stored = data; - } - - @Nullable - @Override - public Object getExtraData() { - return this.stored; - } - - @Override - public String getXmlTag() { - return Objects.requireNonNull(asView()).getClass().getSimpleName(); - } - - @Override - public void registerNamespace(INamespace namespace) { - this.namespaces.add(namespace); - } - - @Nullable - @Override - public INamespace findRegisteredNamespace(@NonNull String name) { - final var namespaces = - this.namespaces.stream() - .filter(ns -> name.equals(ns.getPrefix())) - .collect(Collectors.toSet()); - - if (namespaces.isEmpty()) { - - // If the namespace was not declared in this view, - // check if it was declared in parent - if (getParent() != null) { - return getParent().findRegisteredNamespace(name); - } - - return null; - } - - return namespaces.iterator().next(); - } - - /** - * Generate the XML layout code for this view. - * - *

NOTE: To avoid writing same logic in multiple files, this implementation tries to handle - * logic for both {@link IView} and {@link IViewGroup}. - * - *

For example, if this view is an {@link IViewGroup}, then the layout code for its children - * will also be printed. - * - * @param indentCount The number of tabs to indent. To define custom tab size, see {@link - * IView#DEFAULT_INDENTATION_LENGTH}. - * @return The generated XML code. Never null. - */ - @NonNull - @Override - public String generateCode(int indentCount) { - final var sb = new StringBuilder(); - sb.append("<"); - sb.append(getXmlTag()); - - if (!attributes.isEmpty()) { - for (var attr : attributes) { - newLine(sb, indentCount + 1); // Attributes must be indented by one tab (4 spaces by - // default) - attr.getNamespace(); - if (attr.getNamespace() != null && attr.getNamespace().getPrefix().length() > 0) { - sb.append(attr.getNamespace().getPrefix()); - sb.append(":"); - } - sb.append(attr.getAttributeName()); - sb.append("="); - sb.append("\""); - sb.append(attr.getValue()); - sb.append("\""); - } - } - - var hasChildren = false; - if (this instanceof IViewGroup) { - final var group = (IViewGroup) this; - hasChildren = group.getChildCount() > 0; - - if (hasChildren) { - sb.append(">"); - newLine(sb, indentCount + 1); - - for (var child : group.getChildren()) { - newLine(sb, indentCount + 1); - sb.append(child.generateCode(indentCount + 1)); - newLine(sb, indentCount + 1); - } - } - } - - if (hasChildren) { - // Closing '>' is already printed, no need to print it here. - // Leave one line and append the closing tag. - newLine(sb, indentCount); - newLine(sb, indentCount); - sb.append(""); - } else { - sb.append("/>"); - } - - return sb.toString(); - } - - protected void newLine(@NonNull StringBuilder stringBuilder, int indentCount) { - stringBuilder.append("\n"); - indent(stringBuilder, indentCount); - } - - protected void indent(StringBuilder sb, int count) { - for (var i = 0; i < DEFAULT_INDENTATION_LENGTH * count; i++) { - sb.append(" "); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseViewGroup.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseViewGroup.java deleted file mode 100644 index 898b0491a..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/BaseViewGroup.java +++ /dev/null @@ -1,112 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater.impl; - -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttributeAdapter; -import com.itsaky.inflater.IView; -import com.itsaky.inflater.IViewGroup; - -import java.util.ArrayList; -import java.util.List; - -public abstract class BaseViewGroup extends BaseView implements IViewGroup { - - protected final List mHierarchyChangeListeners = new ArrayList<>(); - - public BaseViewGroup(String qualifiedName, ViewGroup view) { - this(qualifiedName, view, false); - } - - public BaseViewGroup(String qualifiedName, ViewGroup view, boolean isPlaceholder) { - super(qualifiedName, view, isPlaceholder); - } - - @Override - public boolean isPlaceholder() { - return false; // We do not use view groups as placeholder - } - - @Override - public void addView(IView view, int index) { - for (IAttributeAdapter adapter : getAttributeAdapters()) { - view.registerAttributeAdapter(adapter); - } - } - - @Override - public int indexOfChild(@NonNull IView view) { - return indexOfChild(view.asView()); - } - - @Override - public int indexOfChild(View view) { - for (var i = 0; i < getChildCount(); i++) { - final var child = getChildAt(i); - if (child != null - && child.asView() != null - && child.asView() == view) { // Comparison is same in ViewGroup implementation - return i; - } - } - return -1; - } - - @Override - public void registerHierarchyChangeListener(OnHierarchyChangeListener listener) { - if (listener == null) { - return; - } - this.mHierarchyChangeListeners.add(listener); - } - - @Override - public void unregisterHierarchyChangeListener(OnHierarchyChangeListener listener) { - if (listener == null) { - return; - } - - this.mHierarchyChangeListeners.remove(listener); - } - - /** - * Called when a new view has been added to this group - * - * @param view The view that was added - */ - protected void onViewAdded(IView view) { - for (OnHierarchyChangeListener listener : this.mHierarchyChangeListeners) { - listener.onViewAdded(view); - } - } - - /** - * Called when a new view has been removed from this group - * - * @param view The view that was removed - */ - protected void onViewRemoved(IView view) { - for (OnHierarchyChangeListener listener : this.mHierarchyChangeListeners) { - listener.onViewRemoved(view); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/ErrorUiView.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/ErrorUiView.java deleted file mode 100644 index 1b343a940..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/ErrorUiView.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.impl; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; - -import com.itsaky.inflater.IView; - -import org.jetbrains.annotations.Contract; - -/** - * A view which is used to show views that cannot be inflated. This view will preserve the - * attributes and the children if the view is a view group; - * - * @author Akash Yadav - */ -public class ErrorUiView extends UiViewGroup { - - private ErrorUiView(String qualifiedName, ViewGroup view) { - super(qualifiedName, view); - } - - private ErrorUiView(String qualifiedName, ViewGroup view, boolean isPlaceholder) { - super(qualifiedName, view, isPlaceholder); - } - - /** - * Create a new error view for the given qualified name of the view that was not inflated - * correctly. - * - *

We add a text directly in this view. Any other views that will be added to this group will - * have visibility {@link View#GONE} by default. - * - * @param context The context that will be used to create the view. - * @param name The qualified name of the view that was not inflated. - * @param message The message that should be shown to the user. - * @return A newly created instance of the error view. - */ - @NonNull - @Contract("_, _, _ -> new") - public static IView create( - @NonNull final Context context, @NonNull final String name, @NonNull final String message) { - final var container = new LinearLayout(context); - final var error = new TextView(context); - error.setText(message); - error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2)); - error.setTextColor(ContextCompat.getColor(context, android.R.color.black)); - error.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white)); - - container.setOrientation(LinearLayout.HORIZONTAL); - container.addView( - error, - new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - - return new ErrorUiView(name, container, true); - } - - @Override - public void addView(IView view, int index) { - super.addView(view, index); - view.asView().setVisibility(View.GONE); - } - - @Override - public int getChildCount() { - return super.getChildCount() - 1; - } - - @Override - public String getXmlTag() { - // This might be a custom view or something - // So return the qualified name of the view - return this.qualifiedName; - } - - @NonNull - @Override - public String generateCode(int indentCount) { - return super.generateCode(indentCount); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/IncludeLayout.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/IncludeLayout.java deleted file mode 100644 index b5bab46e7..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/IncludeLayout.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.impl; - -import com.itsaky.inflater.IView; - -/** - * Layout model for <include> tags. - * - * @author Akash Yadav - */ -public class IncludeLayout extends BaseView { - - public static final String TAG = "include"; - private final IView view; - - public IncludeLayout(IView view) { - super(TAG, view.asView(), false); - this.view = view; - } - - @Override - public String getXmlTag() { - return TAG; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiAttribute.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiAttribute.java deleted file mode 100644 index 9baeef8da..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiAttribute.java +++ /dev/null @@ -1,93 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater.impl; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.IAttribute; -import com.itsaky.xml.INamespace; - -import java.util.Objects; - -public class UiAttribute implements IAttribute { - - private final INamespace namespace; - private final String name; - protected String value; - - public UiAttribute(INamespace namespace, String name, String value) { - this.namespace = namespace; - this.name = name; - this.value = value; - } - - @Override - public int hashCode() { - return Objects.hash(getNamespace(), name, getValue()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UiAttribute that = (UiAttribute) o; - return Objects.equals(getNamespace(), that.getNamespace()) - && Objects.equals(name, that.name) - && Objects.equals(getValue(), that.getValue()); - } - - @NonNull - @Override - public INamespace getNamespace() { - return namespace; - } - - @NonNull - @Override - public String getAttributeName() { - return name; - } - - @NonNull - @Override - public String getValue() { - return this.value; - } - - @Override - public void apply(String value) { - this.value = value; - } - - @NonNull - @Override - public String toString() { - return "UiAttribute [" - + "\n" - + " namespace = " - + namespace - + "\n" - + " name = " - + name - + "\n" - + " value = " - + value - + "\n" - + "]"; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiView.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiView.java deleted file mode 100644 index 2222bb931..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiView.java +++ /dev/null @@ -1,31 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater.impl; - -import android.view.View; - -public class UiView extends BaseView { - - public UiView(String qualifiedName, View view) { - this(qualifiedName, view, false); - } - - public UiView(String qualifiedName, View view, boolean isPlaceholder) { - super(qualifiedName, view, isPlaceholder); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiViewGroup.java b/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiViewGroup.java deleted file mode 100644 index 1de762b88..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/impl/UiViewGroup.java +++ /dev/null @@ -1,110 +0,0 @@ -/************************************************************************************ - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - **************************************************************************************/ -package com.itsaky.inflater.impl; - -import android.view.ViewGroup; - -import androidx.annotation.Nullable; - -import com.itsaky.inflater.IView; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -public class UiViewGroup extends BaseViewGroup { - - private final ViewGroup viewGroup; - private final List children; - - public UiViewGroup(String qualifiedName, ViewGroup view) { - this(qualifiedName, view, false); - } - - public UiViewGroup(String qualifiedName, ViewGroup view, boolean isPlaceholder) { - super(qualifiedName, view, isPlaceholder); - - this.viewGroup = view; - this.children = new ArrayList<>(); - } - - @Override - public void addView(IView view) { - addView(view, getChildCount()); - } - - @Override - public void addView(IView view, int index) { - super.addView(view, index); - this.viewGroup.addView(view.asView(), index); - this.children.add(index, view); - - view.setParent(this); - onViewAdded(view); - } - - @Override - public void removeView(int index) { - final IView view = children.get(index); - removeView(view); - } - - @Override - public void removeView(@Nullable IView view) { - if (view == null) { - LOG.warn("Null view passed to IViewGroup.removeView(IView). Ignoring."); - return; - } - - this.viewGroup.removeView(view.asView()); - this.children.remove(view); - - view.setParent(null); - onViewRemoved(view); - } - - @Override - public void removeAll() { - for (var child : this.children) { - // Remove each view individually - // This will make sure that we set the parent of the children to null - // and call #onViewRemoved (IView) - removeView(child); - } - } - - @Override - public void forEachChild(Consumer consumer) { - this.children.stream().forEach(consumer); - } - - @Override - public int getChildCount() { - return this.viewGroup.getChildCount(); - } - - @Override - public List getChildren() { - return this.children; - } - - @Override - public IView getChildAt(int index) { - return children.get(index); - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java b/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java deleted file mode 100644 index e068ecfe8..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.util; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.core.view.GravityCompat; - -import com.itsaky.androidide.app.BaseApplication; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.IResourceTable; -import com.itsaky.inflater.drawable.DrawableParserFactory; - -import java.io.File; -import java.lang.reflect.Field; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * Contains methods which are commonly used by layout inflater, attribute adapters and drawable - * parsers. - * - * @author Akash Yadav - */ -public class CommonParseUtils { - - private static final Pattern HEX_COLOR = Pattern.compile("#[a-fA-F0-9]{6,8}"); - private static final ILogger LOG = ILogger.newInstance("CommonParseUtils"); - protected final DisplayMetrics displayMetrics; - protected IResourceTable resourceFinder; - - public CommonParseUtils(@NonNull IResourceTable resourceFinder, DisplayMetrics displayMetrics) { - this.resourceFinder = resourceFinder; - this.displayMetrics = displayMetrics; - } - - public int parseColor(String color, final Context ctx) { - if (HEX_COLOR.matcher(color).matches()) { - try { - return Color.parseColor(color); - } catch (Throwable th) { - // Ignored - } - } else if (color.startsWith("@color/")) { - - return parseColor(resourceFinder.findColor(color.substring("@color/".length())), ctx); - } else if (color.startsWith("@android:color/")) { - final int id = findFrameworkResourceId("color", color.substring("@android:color/".length())); - return ContextCompat.getColor(ctx, id); - } - - return Color.TRANSPARENT; - } - - protected String[] parseArray(final String value) { - if (value.startsWith("@android:array/")) { - final var name = value.substring("@android:array/".length()); - return frameworkArrayValue(name); - } else if (value.startsWith("@array/")) { - final var name = value.substring("@array/".length()); - return resourceFinder.findArray(name); - } else { - return new String[] {value}; - } - } - - private String[] frameworkArrayValue(String name) { - try { - final var id = findFrameworkResourceId("array", name); - if (id == -1) { - return new String[] {name}; - } - - return BaseApplication.getBaseInstance().getResources().getStringArray(id); - } catch (Throwable th) { - return new String[0]; - } - } - - private int findFrameworkResourceId(String type, String name) { - try { - final Class typeClass = - Objects.requireNonNull( - android.R.class.getClassLoader(), - "Unable to get class loader for loading system resources") - .loadClass("android.R$" + type); - final Field typeField = typeClass.getDeclaredField(name); - typeField.setAccessible(true); - return typeField.getInt(null); - } catch (Throwable th) { - LOG.error("Unable to find framework resource.", "type=" + type, "name=" + name); - return -1; - } - } - - protected String parseString(@NonNull String value) { - if (value.startsWith("@string/")) { - return parseString(resourceFinder.findString(value.substring("@string/".length()))); - } else if (value.startsWith("@android:string/")) { - return frameworkStringValue(value.substring("@android:string/".length())); - } else { - return value; - } - } - - protected boolean parseBoolean(@NonNull String value) { - if (!value.startsWith("@")) { - if ("true".equals(value)) { - return true; - } else if ("false".equals(value)) { - return false; - } - } else if (value.startsWith("@bool/")) { - return parseBoolean(resourceFinder.findBoolean(value.substring("@bool/".length()))); - } else if (value.startsWith("@android:bool/")) { - final var name = value.substring("@android:bool/".length()); - return frameworkBooleanValue(name); - } - return false; - } - - protected int parseInteger(@NonNull String value, int defaultVal) { - if (value.startsWith("@integer/")) { - return parseInteger( - resourceFinder.findInteger(value.substring("@integer/".length())), defaultVal); - } else if (value.startsWith("@android:integer/")) { - final var name = value.substring("@android:integer/".length()); - return frameworkIntegerResource(name, defaultVal); - } else { - try { - return Integer.parseInt(value); - } catch (Throwable th) { - return defaultVal; - } - } - } - - protected int parseDimension(final String value, int defaultValue) { - if (value == null || value.isEmpty()) { - return defaultValue; - } - - char c = value.charAt(0); - if (Character.isDigit(c)) { - // A dimension value which starts with a digit. E.g.: 1dp, 12sp, 123px, etc. - StringBuilder dimensionVal = new StringBuilder(); - int index = 0; - while (Character.isDigit(c = value.charAt(index))) { - dimensionVal.append(c); - index++; - } - - final int dimen = Integer.parseInt(dimensionVal.toString()); - final String dimensionType = value.substring(index); - return (int) - TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, displayMetrics); - } else if (c == '@') { - if (value.startsWith("@android:dimen/")) { - final var name = value.substring("@android:dimen/".length()); - return frameworkDimensionValue(name, defaultValue); - } else if (value.startsWith("@dimen/")) { - String name = value.substring("@dimen/".length()); - return parseDimension(resourceFinder.findDimension(name), defaultValue); - } - } else if (Character.isLetter(c)) { - // This could be one of the following : - // 1. match_parent - // 2. wrap_content - // 3. fill_parent - switch (value) { - case "match_parent": - case "fill_parent": - return ViewGroup.LayoutParams.MATCH_PARENT; - case "wrap_content": - default: - return ViewGroup.LayoutParams.WRAP_CONTENT; - } - } - - return defaultValue; - } - - protected int getUnitForDimensionType(@NonNull String dimensionType) { - - switch (dimensionType) { - case "dp": - return TypedValue.COMPLEX_UNIT_DIP; - case "sp": - return TypedValue.COMPLEX_UNIT_SP; - case "px": - return TypedValue.COMPLEX_UNIT_PX; - case "pt": - return TypedValue.COMPLEX_UNIT_PT; - case "in": - return TypedValue.COMPLEX_UNIT_IN; - case "mm": - return TypedValue.COMPLEX_UNIT_MM; - } - - return TypedValue.COMPLEX_UNIT_DIP; - } - - protected ColorStateList parseColorStateList(@NonNull String value, Context context) { - if (HEX_COLOR.matcher(value).matches()) { - return ColorStateList.valueOf(Color.parseColor(value)); - } else if (value.startsWith("@color/")) { - final var name = value.substring("@color/".length()); - final var res = resourceFinder.findColor(name); - final var color = parseColor(res, context); - return ColorStateList.valueOf(color); - } else if (value.startsWith("@android:color/")) { - final var name = value.substring("@android:color/".length()); - final var id = findFrameworkResourceId("color", name); - if (id == -1) { - return ColorStateList.valueOf(Color.TRANSPARENT); - } - try { - return ContextCompat.getColorStateList(context, id); - } catch (Throwable th) { - try { - return ColorStateList.valueOf(ContextCompat.getColor(context, id)); - } catch (Throwable th2) { - LOG.error("Unable to create color state list for framework resource", name); - return ColorStateList.valueOf(Color.TRANSPARENT); - } - } - } - - return ColorStateList.valueOf(Color.TRANSPARENT); - } - - protected Drawable parseDrawable(String value, final Context ctx) { - if (HEX_COLOR.matcher(value).matches()) { - return drawableForColor(value); - } else if (value.startsWith("@")) { - - // First check if this is a reference to an android resource - if (value.startsWith("@android:")) { - final String typeAndValue = value.substring("@android:".length()); - final String[] split = - typeAndValue.split( - Pattern.quote("/")); // For @android:color/white, it will be ["color", - // "white"] - final String type = split[0]; - final String typeVal = split[1]; - final int id = findFrameworkResourceId(type, typeVal); - - if (id != -1) { - switch (type) { - case "color": - return drawableForColor(ContextCompat.getColor(ctx, id)); - case "drawable": - return ContextCompat.getDrawable(ctx, id); - default: - return newTransparentDrawable(); - } - } - - } else { - // We found a reference to another resource - if (value.startsWith("@drawable/")) { - final File drawable = resourceFinder.findDrawable(value.substring("@drawable/".length())); - - if (drawable == null) { - return null; - } - - try { - final var parser = DrawableParserFactory.newParser(ctx, drawable, resourceFinder); - if (parser == null) { - return null; - } - return parser.parse(); - } catch (Exception e) { - LOG.error("Error parsing drawable", e); - return null; - } - - } else if (value.startsWith("@color/")) { - final String color = resourceFinder.findColor(value.substring("@color/".length())); - return new ColorDrawable(parseColor(color, ctx)); - } - } - } - return newTransparentDrawable(); - } - - protected Drawable drawableForColor(String color) { - try { - return drawableForColor(Color.parseColor(color)); - } catch (Throwable th) { - return newTransparentDrawable(); - } - } - - protected Drawable drawableForColor(int color) { - return new ColorDrawable(color); - } - - protected Drawable newTransparentDrawable() { - return new ColorDrawable(Color.TRANSPARENT); - } - - protected int parseGravity(@NonNull String value) { - final String[] splits = value.split(Pattern.quote("|")); - int result = -1; - for (String split : splits) { - final int gravity = gravityFor(split); - if (result == -1) { - result = gravity; - } else { - result |= gravity; - } - } - return result; - } - - protected int gravityFor(@NonNull String gravity) { - switch (gravity) { - case "center": - return Gravity.CENTER; - case "center_vertical": - return Gravity.CENTER_VERTICAL; - case "center_horizontal": - return Gravity.CENTER_HORIZONTAL; - case "left": - return Gravity.LEFT; - case "right": - return Gravity.RIGHT; - case "top": - return Gravity.TOP; - case "bottom": - return Gravity.BOTTOM; - case "start": - return GravityCompat.START; - case "end": - return GravityCompat.END; - default: - return Gravity.TOP | Gravity.START; - } - } - - private String frameworkStringValue(String name) { - try { - final var id = findFrameworkResourceId("string", name); - if (id == -1) { - return ""; - } - - return BaseApplication.getBaseInstance().getString(id); - } catch (Throwable th) { - return ""; - } - } - - private boolean frameworkBooleanValue(String name) { - try { - final var id = findFrameworkResourceId("boolean", name); - if (id == -1) { - return false; - } - - return BaseApplication.getBaseInstance().getResources().getBoolean(id); - } catch (Throwable th) { - return false; - } - } - - private int frameworkIntegerResource(String name, int defaultVal) { - try { - final var id = findFrameworkResourceId("integer", name); - if (id == -1) { - return defaultVal; - } - - return BaseApplication.getBaseInstance().getResources().getInteger(id); - } catch (Throwable th) { - return defaultVal; - } - } - - private int frameworkDimensionValue(String name, int defValue) { - try { - final var id = findFrameworkResourceId("dimen", name); - if (id == -1) { - return defValue; - } - - return (int) BaseApplication.getBaseInstance().getResources().getDimension(id); - } catch (Throwable th) { - return defValue; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/util/Preconditions.java b/xml-inflater/src/main/java/com/itsaky/inflater/util/Preconditions.java deleted file mode 100644 index cc6053164..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/util/Preconditions.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater.util; - -import com.itsaky.inflater.InflateException; - -public class Preconditions { - - public static void assertAllNotNull(String msg, Object... objs) { - int i = 0; - for (Object obj : objs) { - if (obj == null) { - throw new NullPointerException("[" + i + "] " + msg); - } - ++i; - } - } - - public static void assertNotBlank(String str, String msg) throws InflateException { - Preconditions.assertNotnull(str, msg); - if (str.trim().length() <= 0) { - throw new InflateException(msg); - } - } - - public static void assertNotnull(Object obj, String msg) throws InflateException { - if (obj == null) { - throw new NullPointerException(msg); - } - } - - public static void assertTrue(boolean condition, String message) { - if (!condition) { - throw new AssertionError(message); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/util/TextDrawable.java b/xml-inflater/src/main/java/com/itsaky/inflater/util/TextDrawable.java deleted file mode 100644 index e6da52f8d..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/util/TextDrawable.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.itsaky.inflater.util; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.util.DisplayMetrics; -import android.util.TypedValue; - -import androidx.annotation.NonNull; - -public class TextDrawable extends Drawable { - private static final int DEFAULT_COLOR = Color.WHITE; - private static final int DEFAULT_TEXT_SIZE = 15; - private final Paint mPaint; - private final CharSequence mText; - private final int mIntrinsicWidth; - private final int mIntrinsicHeight; - - public TextDrawable(CharSequence text, @NonNull DisplayMetrics displayMetrics) { - mText = text; - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mPaint.setColor(DEFAULT_COLOR); - mPaint.setTextAlign(Align.CENTER); - float textSize = - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE, displayMetrics); - mPaint.setTextSize(textSize); - mIntrinsicWidth = (int) (mPaint.measureText(mText, 0, mText.length()) + .5); - mIntrinsicHeight = mPaint.getFontMetricsInt(null); - } - - @Override - public void draw(Canvas canvas) { - Rect bounds = getBounds(); - canvas.drawText(mText, 0, mText.length(), bounds.centerX(), bounds.centerY(), mPaint); - } - - @Override - public void setAlpha(int alpha) { - mPaint.setAlpha(alpha); - } - - @Override - public void setColorFilter(ColorFilter filter) { - mPaint.setColorFilter(filter); - } - - @Override - public int getOpacity() { - return mPaint.getAlpha(); - } - - @Override - public int getIntrinsicWidth() { - return mIntrinsicWidth; - } - - @Override - public int getIntrinsicHeight() { - return mIntrinsicHeight; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/FrameworkValues.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/FrameworkValues.java deleted file mode 100644 index 210b87d52..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/FrameworkValues.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.values; - -import static java.lang.reflect.Modifier.isPublic; -import static java.lang.reflect.Modifier.isStatic; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * Provides framework resource values. - * - * @author Akash Yadav - */ -public class FrameworkValues { - - @NonNull - public static List listDimens() { - return listFields(android.R.dimen.class); - } - - @NonNull - private static List listFields(final Class klass) { - Objects.requireNonNull(klass); - - final var list = new ArrayList(); - final var fields = klass.getDeclaredFields(); - for (var field : fields) { - final var mods = field.getModifiers(); - if (!isPublic(mods) || !isStatic(mods)) { - continue; - } - - list.add(field.getName()); - } - - return list; - } - - @NonNull - public static List listBools() { - return listFields(android.R.bool.class); - } - - @NonNull - public static List listStrings() { - return listFields(android.R.string.class); - } - - @NonNull - public static List listColors() { - return listFields(android.R.color.class); - } - - @NonNull - public static List listAllResources() { - final var list = new ArrayList(); - for (var klass : android.R.class.getDeclaredClasses()) { - list.addAll( - listFields(klass).stream() - .map(("@android:" + klass.getSimpleName() + "/")::concat) - .collect(Collectors.toList())); - } - return list; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/IResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/IResource.java deleted file mode 100644 index 4ce7fe3dc..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/IResource.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.values; - -import androidx.annotation.NonNull; - -/** - * Base class for value entries in "res/values" directory. - * - * @author Akash Yadav - */ -public interface IResource { - - /** - * Get the unique identifier (name) of this resource. Defined by "name" attribute in resource - * value entries in xml files. - * - * @return The name of this value. - */ - @NonNull - String getName(); - - /** - * Get the value of this resource entry. - * - * @return The value of this resource. - */ - @NonNull - String getValue(); -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTable.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTable.java deleted file mode 100644 index 87ddc8992..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTable.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - * - */ -package com.itsaky.inflater.values; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.blankj.utilcode.util.FileUtils; -import com.itsaky.androidide.utils.ILogger; -import com.itsaky.inflater.values.models.ArrayResource; -import com.itsaky.inflater.values.models.BooleanResource; -import com.itsaky.inflater.values.models.ColorResource; -import com.itsaky.inflater.values.models.DimensionResource; -import com.itsaky.inflater.values.models.IntegerResource; -import com.itsaky.inflater.values.models.StringResource; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; - -/** - * Reads and manages values (colors, strings, integers, etc.) for a single module. - * - * @author Akash Yadav - */ -public class ValuesTable { - - private static final ILogger LOG = ILogger.newInstance("ValuesTable"); - // TODO Parse styles and styleables - private final Map arrays; - private final Map strings; - private final Map colors; - private final Map integers; - private final Map dimens; - private final Map booleans; - - private final Map> resourceMap = new HashMap<>(); - - protected ValuesTable() { - this.arrays = new HashMap<>(); - this.strings = new HashMap<>(); - this.colors = new HashMap<>(); - this.integers = new HashMap<>(); - this.dimens = new HashMap<>(); - this.booleans = new HashMap<>(); - - this.resourceMap.put("array", this.arrays); - this.resourceMap.put("string", this.strings); - this.resourceMap.put("color", this.colors); - this.resourceMap.put("integer", this.integers); - this.resourceMap.put("dimen", this.dimens); - this.resourceMap.put("bool", this.booleans); - } - - /** - * Create a new table by reading the files from the given values directory. - * - * @param valuesDirectory The "res/values" directory. - * @return The values table or {@code null} if the directory is invalid or there are no files in - * this directory. - * @throws XmlPullParserException Thrown by {@link XmlPullParserFactory}. - * @throws IOException Thrown by {@link XmlPullParser}. - */ - @Nullable - public static ValuesTable forDirectory(File valuesDirectory) throws Exception { - if (valuesDirectory == null) { - LOG.error("Cannot create values table for directory: ", null); - return null; - } - - if (!valuesDirectory.exists()) { - LOG.error( - "Cannot create values table for directory as it does not exists. Directory:", - valuesDirectory); - return null; - } - - final var files = valuesDirectory.listFiles(ValuesTable::isValid); - if (files == null) { - LOG.error("No files found in the given values directory"); - return null; - } - - final var start = System.currentTimeMillis(); - final var values = new ValuesTable(); - - for (var file : files) { - final var factory = XmlPullParserFactory.newInstance(); - final var parser = factory.newPullParser(); - parser.setInput(new FileInputStream(file), null); - - var event = parser.getEventType(); - while (event != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final var tag = parser.getName(); - if ("resources".equals(tag)) { - event = parser.next(); - continue; - } - - readTag(tag, parser, values); - } - event = parser.next(); - } - } - - final var duration = System.currentTimeMillis() - start; - LOG.info( - String.format( - Locale.getDefault(), - "Took %d ms to read %d files in directory: %s", - duration, - files.length, - valuesDirectory)); - - return values; - } - - private static void readTag(String tag, XmlPullParser parser, ValuesTable values) - throws Exception { - - if ("style".equals(tag) || "declare-styleable".equals(tag)) { - // These tags are not supported yet - // Skip parsing until we reach next tag with same depth as of this tag - skip(parser); - return; - } - - // TODO Should we check if these tags are specified within ? - final var name = getName(parser); - if (name == null) { - LOG.error(String.format("A <%s> resource was found with no 'name' attribute", tag)); - return; - } - - if ("item".equals(tag)) { - tag = parser.getAttributeValue(null, "type"); - if (tag == null) { - LOG.error(" resource value found but no 'type' was specified."); - return; - } - } - - // check for array before reading value - // because array resources do not have text values - if ("array".equals(tag) || tag.endsWith("-array")) { - readArray(parser, values, name); - return; - } - - // Value must be read after all other required values have been read - final var value = parser.nextText(); - IResource resourceValue; - - switch (tag) { - case "string": - resourceValue = new StringResource(name, value); - values.strings.put(name, resourceValue); - break; - case "color": - resourceValue = new ColorResource(name, value); - values.colors.put(name, resourceValue); - break; - case "bool": - resourceValue = new BooleanResource(name, value); - values.booleans.put(name, resourceValue); - break; - case "dimen": - resourceValue = new DimensionResource(name, value); - values.dimens.put(name, resourceValue); - break; - case "integer": - resourceValue = new IntegerResource(name, value); - values.integers.put(name, resourceValue); - break; - default: - LOG.warn("Unknown or unsupported resource value tag:", tag); - break; - } - } - - private static void readArray(@NonNull XmlPullParser parser, ValuesTable values, String name) - throws XmlPullParserException, IOException { - final var minDepth = parser.getDepth(); - final var items = new ArrayList(); - - var event = parser.getEventType(); - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT && parser.getDepth() >= minDepth) { - if (event == XmlPullParser.START_TAG) { - final var child = parser.getName(); - if ("item".equals(child)) { - final var value = parser.nextText(); - items.add(value); - } - } - } - - final var arr = new String[items.size()]; - for (int i = 0; i < arr.length; i++) { - arr[i] = items.get(i); - } - - values.arrays.put(name, new ArrayResource(name, arr)); - } - - private static void skip(@NonNull XmlPullParser parser) - throws XmlPullParserException, IOException { - if (parser.getEventType() != XmlPullParser.START_TAG) { - throw new IllegalStateException(); - } - int depth = 1; - while (depth != 0) { - switch (parser.next()) { - case XmlPullParser.END_TAG: - depth--; - break; - case XmlPullParser.START_TAG: - depth++; - break; - } - } - } - - private static String getName(@NonNull XmlPullParser parser) { - return parser.getAttributeValue(null, "name"); - } - - private static boolean isValid(File file) { - return file != null && file.isFile() && FileUtils.isUtf8(file); - } - - @Nullable - public Map getTable(final String name) { - Objects.requireNonNull(name); - return this.resourceMap.get(name); - } - - @NonNull - public Map> getResourceMap() { - return resourceMap; - } - - /** - * Finds a resource value of any type. - * - * @param name The name of the resource to find. - * @return The resource value or {@code null} if there is no resource with the given name. - */ - @SuppressWarnings("unused") - public IResource findResource(final String name) { - IResource value; - - value = findString(name); - if (value != null) { - return value; - } - - value = findColor(name); - if (value != null) { - return value; - } - - value = findInteger(name); - if (value != null) { - return value; - } - - value = findDimension(name); - if (value != null) { - return value; - } - - value = findArray(name); - if (value != null) { - return value; - } - - value = findBoolean(name); - //noinspection RedundantIfStatement - if (value != null) { - return value; - } - - return null; - } - - public ArrayResource findArray(final String name) { - return (ArrayResource) arrays.getOrDefault(name, null); - } - - /** - * Find a string value with the given name. - * - * @param name The name of the string resource. - * @return The string resource or {@code null} if there is no resource with the given name. - */ - public StringResource findString(final String name) { - return (StringResource) strings.getOrDefault(name, null); - } - - /** - * Find a color value with the given name. - * - * @param name The name of the color resource. - * @return The color resource or {@code null} if there is no resource with the given name. - */ - public ColorResource findColor(final String name) { - return (ColorResource) colors.getOrDefault(name, null); - } - - /** - * Find a integer value with the given name. - * - * @param name The name of the integer resource. - * @return The integer resource or {@code null} if there is no resource with the given name. - */ - public IntegerResource findInteger(final String name) { - return (IntegerResource) integers.getOrDefault(name, null); - } - - /** - * Find a dimension value with the given name. - * - * @param name The name of the dimension resource. - * @return The dimension resource or {@code null} if there is no resource with the given name. - */ - public DimensionResource findDimension(final String name) { - return (DimensionResource) dimens.getOrDefault(name, null); - } - - /** - * Find a boolean value with the given name. - * - * @param name The name of the boolean resource. - * @return The boolean resource or {@code null} if there is no resource with the given name. - */ - public BooleanResource findBoolean(final String name) { - return (BooleanResource) booleans.getOrDefault(name, null); - } - - /** - * Reads the contents of the given file and updates this table. - * - * @param file The file to sync with. - */ - public void syncWithFile(final File file) throws Exception { - final var start = System.currentTimeMillis(); - final var factory = XmlPullParserFactory.newInstance(); - final var parser = factory.newPullParser(); - parser.setInput(new FileInputStream(file), null); - - var event = parser.getEventType(); - while (event != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final var tag = parser.getName(); - if ("resources".equals(tag)) { - event = parser.next(); - continue; - } - - ValuesTable.readTag(tag, parser, this); - } - event = parser.next(); - } - - LOG.debug( - "Syncing with file", file.getName(), "took", (System.currentTimeMillis() - start), "ms"); - } - - @NonNull - @Override - public String toString() { - return "ValuesTable{" - + "strings=" - + strings - + ", colors=" - + colors - + ", integers=" - + integers - + ", dimens=" - + dimens - + ", booleans=" - + booleans - + '}'; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTableFactory.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTableFactory.java deleted file mode 100644 index ebc1c5791..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/ValuesTableFactory.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.values; - -import com.itsaky.androidide.utils.ILogger; - -import java.io.File; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * A factory class that reads the files in the "values" directory of given "res" directories. - * - * @author Akash Yadav - */ -public class ValuesTableFactory { - - private static final Map valueTables = new HashMap<>(); - private static final ILogger LOG = ILogger.newInstance("ValuesTableFactory"); - - public static ValuesTable getTable(final File resDir) { - return valueTables.getOrDefault(resDir, null); - } - - public static Map getAllTables() { - return valueTables; - } - - /** - * Setup this ValuesTableFactory with the given "res" directories. - * - * @param resDirs The resource directories. - */ - public static void setupWithResDirectories(File... resDirs) { - - if (resDirs == null || resDirs.length <= 0) { - LOG.error("Cannot create value tables. No directories were specified."); - return; - } - - valueTables.clear(); - - final var start = System.currentTimeMillis(); - for (var res : resDirs) { - if (res == null || !res.exists()) { - continue; - } - - final var table = createTable(res); - if (table == null) { - continue; - } - - valueTables.put(res, table); - } - - LOG.debug( - String.format( - Locale.getDefault(), - "Created value tables for %d resource directories in %d ms", - resDirs.length, - (System.currentTimeMillis() - start))); - } - - private static ValuesTable createTable(File res) { - try { - final var values = new File(res, "values"); - if (!values.exists()) { - return null; - } - - var table = ValuesTable.forDirectory(values); - if (table == null) { - throw new ParseException(); - } - - return table; - } catch (Throwable e) { - LOG.error("Failed to create values table", e); - return null; - } - } - - /** - * Notify that the given file's contents were changed and the corresponding table must be updated - * with new values. - * - * @param file The file that was changed. - */ - public static void syncWithFile(final File file) { - if (file == null) { - LOG.error("Cannot update value tables. Given file is null."); - return; - } - - final var pattern = Pattern.compile(".*/src/.*/res/values/(\\w|_)+\\.xml"); - if (!pattern.matcher(file.getAbsolutePath()).matches()) { - // This file is not a values resource file. - return; - } - - final var resDir = - Objects.requireNonNull(file.getParentFile()) /* values dir */.getParentFile() /* res dir*/; - var table = valueTables.getOrDefault(resDir, null); - if (table == null) { - table = createTable(resDir); - if (table != null) { - valueTables.put(resDir, table); - } - return; - } - - try { - table.syncWithFile(file); - } catch (Throwable e) { - LOG.error("Failed to sync values table", e); - } - } - - static class ParseException extends RuntimeException {} -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/AbstractResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/AbstractResource.java deleted file mode 100644 index da5aea7c1..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/AbstractResource.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.values.models; - -import androidx.annotation.NonNull; - -import com.itsaky.inflater.values.IResource; - -import java.util.Objects; - -/** - * Abstract class for resource value entries. - * - * @author Akash Yadav - */ -abstract class AbstractResource implements IResource { - - private final String name; - private final String value; - - public AbstractResource(@NonNull String name, @NonNull String value) { - this.name = name; - this.value = value; - } - - @Override - public int hashCode() { - return Objects.hash(getName(), getValue()); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof AbstractResource)) { - return false; - } - - AbstractResource that = (AbstractResource) o; - return Objects.equals(getName(), that.getName()) && Objects.equals(getValue(), that.getValue()); - } - - @NonNull - @Override - public String getName() { - return name; - } - - @NonNull - @Override - public String getValue() { - return value; - } - - @NonNull - @Override - public String toString() { - return getClass().getSimpleName() - + "{" - + "name='" - + name - + '\'' - + ", value='" - + value - + '\'' - + '}'; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ArrayResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ArrayResource.java deleted file mode 100644 index 352c14c7d..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ArrayResource.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.values.models; - -import android.graphics.Color; -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -/** - * An array resource. Can be any of the following : - * - *

    - *
  • <integer-array> - *
  • <string-array> - *
  • <array> - *
- * - * @author Akash Yadav - */ -public class ArrayResource extends AbstractResource { - - private final String[] values; - - public ArrayResource(@NonNull String name, @NonNull String[] values) { - super(name, TextUtils.join(", ", values)); - this.values = values; - } - - public String[] getValues() { - return values; - } - - public int[] getIntValues() { - final var ints = new int[values.length]; - for (int i = 0; i < ints.length; i++) { - var value = values[i]; - if (value == null || value.trim().length() == 0) { - value = "0"; - } - - ints[i] = Integer.parseInt(value); - } - - return ints; - } - - public int[] getColorValues() { - final var colors = new int[this.values.length]; - for (int i = 0; i < colors.length; i++) { - var val = values[i]; - if (val == null || val.trim().length() == 0) { - val = "#ffffff"; - } - - colors[i] = Color.parseColor(val); - } - - return colors; - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/BooleanResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/BooleanResource.java deleted file mode 100644 index 9100ab820..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/BooleanResource.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.inflater.values.models; - -import androidx.annotation.NonNull; - -/** - * @author Akash Yadav - */ -public class BooleanResource extends AbstractResource { - - public BooleanResource(@NonNull String name, @NonNull String value) { - super(name, value); - } - - /** - * Get the value of this resource as boolean. - * - * @return The value of this resource. - * @throws UnsupportedOperationException If the value is not a boolean value. This will also be - * thrown if the value this resource refers to another resource. - */ - public boolean asBoolean() { - final var val = getValue(); - switch (val) { - case "true": - return true; - case "false": - return false; - default: - throw new UnsupportedOperationException("Cannot parse boolean value: " + val); - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ColorResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ColorResource.java deleted file mode 100644 index 155b666a6..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/ColorResource.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.values.models; - -import android.graphics.Color; - -import androidx.annotation.NonNull; - -/** - * Represents a color resource value. - * - * @author Akash Yadav - */ -public class ColorResource extends AbstractResource { - - public ColorResource(@NonNull String name, @NonNull String value) { - super(name, value); - } - - /** - * Parse the value of this color resource and get the int color. - * - * @return The int color value or -1 if this is not a valid color. - */ - public int parseColor() { - try { - return Color.parseColor(getValue()); - } catch (Throwable th) { - return -1; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/IntegerResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/IntegerResource.java deleted file mode 100644 index 2e12cc460..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/IntegerResource.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.values.models; - -import androidx.annotation.NonNull; - -/** - * Represents an integer value. - * - * @author Akash Yadav - */ -public class IntegerResource extends AbstractResource { - - public IntegerResource(@NonNull String name, @NonNull String value) { - super(name, value); - } - - /** - * Get the value as integer. Or -1 if the value is not an integer. - * - * @return The integer value. - */ - public int asInteger() { - try { - return Integer.parseInt(getValue()); - } catch (Throwable th) { - return -1; - } - } -} diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/StringResource.java b/xml-inflater/src/main/java/com/itsaky/inflater/values/models/StringResource.java deleted file mode 100644 index 2385f825b..000000000 --- a/xml-inflater/src/main/java/com/itsaky/inflater/values/models/StringResource.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.itsaky.inflater.values.models; - -import androidx.annotation.NonNull; - -/** - * Represents a string value. - * - * @author Akash Yadav - */ -public class StringResource extends AbstractResource { - - public StringResource(@NonNull String name, @NonNull String value) { - super(name, value); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/DefaultValues.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/DefaultValues.java deleted file mode 100644 index 9e051f6e5..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/DefaultValues.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.sdsmdg.harjot.vectormaster; - -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; - -public class DefaultValues { - - public static final int PATH_FILL_COLOR = Color.TRANSPARENT; - public static final int PATH_STROKE_COLOR = Color.TRANSPARENT; - public static final float PATH_STROKE_WIDTH = 0.0f; - public static final float PATH_STROKE_ALPHA = 1.0f; - public static final float PATH_FILL_ALPHA = 1.0f; - public static final Paint.Cap PATH_STROKE_LINE_CAP = Paint.Cap.BUTT; - public static final Paint.Join PATH_STROKE_LINE_JOIN = Paint.Join.MITER; - public static final float PATH_STROKE_MITER_LIMIT = 4.0f; - public static final float PATH_STROKE_RATIO = 1.0f; - // WINDING fill type is equivalent to NON_ZERO - public static final Path.FillType PATH_FILL_TYPE = Path.FillType.WINDING; - public static final float PATH_TRIM_PATH_START = 0.0f; - public static final float PATH_TRIM_PATH_END = 1.0f; - public static final float PATH_TRIM_PATH_OFFSET = 0.0f; - public static final float VECTOR_VIEWPORT_WIDTH = 0.0f; - public static final float VECTOR_VIEWPORT_HEIGHT = 0.0f; - public static final float VECTOR_WIDTH = 0.0f; - public static final float VECTOR_HEIGHT = 0.0f; - public static final float VECTOR_ALPHA = 1.0f; - public static final float GROUP_ROTATION = 0.0f; - public static final float GROUP_PIVOT_X = 0.0f; - public static final float GROUP_PIVOT_Y = 0.0f; - public static final float GROUP_SCALE_X = 1.0f; - public static final float GROUP_SCALE_Y = 1.0f; - public static final float GROUP_TRANSLATE_X = 0.0f; - public static final float GROUP_TRANSLATE_Y = 0.0f; - public static String[] PATH_ATTRIBUTES = { - "name", - "fillAlpha", - "fillColor", - "fillType", - "pathData", - "strokeAlpha", - "strokeColor", - "strokeLineCap", - "strokeLineJoin", - "strokeMiterLimit", - "strokeWidth" - }; -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterDrawable.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterDrawable.java deleted file mode 100644 index 91aa56bbe..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterDrawable.java +++ /dev/null @@ -1,564 +0,0 @@ -package com.sdsmdg.harjot.vectormaster; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Matrix; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; - -import androidx.annotation.NonNull; - -import com.blankj.utilcode.util.FileIOUtils; -import com.sdsmdg.harjot.vectormaster.models.ClipPathModel; -import com.sdsmdg.harjot.vectormaster.models.GroupModel; -import com.sdsmdg.harjot.vectormaster.models.PathModel; -import com.sdsmdg.harjot.vectormaster.models.VectorModel; -import com.sdsmdg.harjot.vectormaster.utilities.Utils; - -import org.jetbrains.annotations.Contract; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import java.io.File; -import java.io.IOException; -import java.io.StringReader; -import java.util.Stack; - -public class VectorMasterDrawable extends Drawable { - - private VectorModel vectorModel; - - private int resID = -1; - private boolean useLegacyParser = true; - - private float offsetX = 0.0f, offsetY = 0.0f; - private float scaleX = 1.0f, scaleY = 1.0f; - private Matrix scaleMatrix; - - private float scaleRatio, strokeRatio; - private int width = -1, height = -1; - private int left = 0, top = 0; - private XmlPullParser xpp; - - public VectorMasterDrawable(Context context, int resID) { - this(context, resID, 0.0f, 0.0f); - } - - public VectorMasterDrawable(Context context, int resID, float offsetX, float offsetY) { - this(context, resID, offsetX, offsetY, 1.0f, 1.0f); - } - - public VectorMasterDrawable( - @NonNull Context context, - int resID, - float offsetX, - float offsetY, - float scaleX, - float scaleY) { - this.resID = resID; - this.offsetX = offsetX; - this.offsetY = offsetY; - this.scaleX = scaleX; - this.scaleY = scaleY; - - final var resources = context.getResources(); - - if (resID == -1) { - vectorModel = null; - return; - } - - this.xpp = resources.getXml(resID); - buildVectorModel(); - } - - private void buildVectorModel() { - int tempPosition; - PathModel pathModel = new PathModel(); - vectorModel = new VectorModel(); - GroupModel groupModel = new GroupModel(); - ClipPathModel clipPathModel = new ClipPathModel(); - Stack groupModelStack = new Stack<>(); - - try { - int event = xpp.getEventType(); - while (event != XmlPullParser.END_DOCUMENT) { - String name = xpp.getName(); - switch (event) { - case XmlPullParser.START_TAG: - switch (name) { - case "vector": - tempPosition = getAttrPosition(xpp, "viewportWidth"); - vectorModel.setViewportWidth( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_VIEWPORT_WIDTH); - - tempPosition = getAttrPosition(xpp, "viewportHeight"); - vectorModel.setViewportHeight( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_VIEWPORT_HEIGHT); - - tempPosition = getAttrPosition(xpp, "alpha"); - vectorModel.setAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_ALPHA); - - tempPosition = getAttrPosition(xpp, "name"); - vectorModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "width"); - vectorModel.setWidth( - (tempPosition != -1) - ? Utils.getFloatFromDimensionString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_WIDTH); - - tempPosition = getAttrPosition(xpp, "height"); - vectorModel.setHeight( - (tempPosition != -1) - ? Utils.getFloatFromDimensionString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_HEIGHT); - break; - case "path": - pathModel = new PathModel(); - - tempPosition = getAttrPosition(xpp, "name"); - pathModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "fillAlpha"); - pathModel.setFillAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_ALPHA); - - tempPosition = getAttrPosition(xpp, "fillColor"); - pathModel.setFillColor( - (tempPosition != -1) - ? Utils.getColorFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_COLOR); - - tempPosition = getAttrPosition(xpp, "fillType"); - pathModel.setFillType( - (tempPosition != -1) - ? Utils.getFillTypeFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_TYPE); - - tempPosition = getAttrPosition(xpp, "pathData"); - pathModel.setPathData( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "strokeAlpha"); - pathModel.setStrokeAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_ALPHA); - - tempPosition = getAttrPosition(xpp, "strokeColor"); - pathModel.setStrokeColor( - (tempPosition != -1) - ? Utils.getColorFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_COLOR); - - tempPosition = getAttrPosition(xpp, "strokeLineCap"); - pathModel.setStrokeLineCap( - (tempPosition != -1) - ? Utils.getLineCapFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_LINE_CAP); - - tempPosition = getAttrPosition(xpp, "strokeLineJoin"); - pathModel.setStrokeLineJoin( - (tempPosition != -1) - ? Utils.getLineJoinFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_LINE_JOIN); - - tempPosition = getAttrPosition(xpp, "strokeMiterLimit"); - pathModel.setStrokeMiterLimit( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_MITER_LIMIT); - - tempPosition = getAttrPosition(xpp, "strokeWidth"); - pathModel.setStrokeWidth( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_WIDTH); - - tempPosition = getAttrPosition(xpp, "trimPathEnd"); - pathModel.setTrimPathEnd( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_END); - - tempPosition = getAttrPosition(xpp, "trimPathOffset"); - pathModel.setTrimPathOffset( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_OFFSET); - - tempPosition = getAttrPosition(xpp, "trimPathStart"); - pathModel.setTrimPathStart( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_START); - - pathModel.buildPath(useLegacyParser); - break; - case "group": - groupModel = new GroupModel(); - - tempPosition = getAttrPosition(xpp, "name"); - groupModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "pivotX"); - groupModel.setPivotX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_PIVOT_X); - - tempPosition = getAttrPosition(xpp, "pivotY"); - groupModel.setPivotY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_PIVOT_Y); - - tempPosition = getAttrPosition(xpp, "rotation"); - groupModel.setRotation( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_ROTATION); - - tempPosition = getAttrPosition(xpp, "scaleX"); - groupModel.setScaleX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_SCALE_X); - - tempPosition = getAttrPosition(xpp, "scaleY"); - groupModel.setScaleY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_SCALE_Y); - - tempPosition = getAttrPosition(xpp, "translateX"); - groupModel.setTranslateX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_TRANSLATE_X); - - tempPosition = getAttrPosition(xpp, "translateY"); - groupModel.setTranslateY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_TRANSLATE_Y); - - groupModelStack.push(groupModel); - break; - case "clip-path": - clipPathModel = new ClipPathModel(); - - tempPosition = getAttrPosition(xpp, "name"); - clipPathModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "pathData"); - clipPathModel.setPathData( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - clipPathModel.buildPath(useLegacyParser); - break; - } - break; - - case XmlPullParser.END_TAG: - switch (name) { - case "path": - if (groupModelStack.size() == 0) { - vectorModel.addPathModel(pathModel); - } else { - groupModelStack.peek().addPathModel(pathModel); - } - vectorModel.getFullpath().addPath(pathModel.getPath()); - break; - case "clip-path": - if (groupModelStack.size() == 0) { - vectorModel.addClipPathModel(clipPathModel); - } else { - groupModelStack.peek().addClipPathModel(clipPathModel); - } - break; - case "group": - GroupModel topGroupModel = groupModelStack.pop(); - if (groupModelStack.size() == 0) { - topGroupModel.setParent(null); - vectorModel.addGroupModel(topGroupModel); - } else { - topGroupModel.setParent(groupModelStack.peek()); - groupModelStack.peek().addGroupModel(topGroupModel); - } - break; - case "vector": - vectorModel.buildTransformMatrices(); - break; - } - break; - } - event = xpp.next(); - } - } catch (XmlPullParserException | IOException e) { - e.printStackTrace(); - } - } - - private int getAttrPosition(@NonNull XmlPullParser xpp, String attrName) { - for (int i = 0; i < xpp.getAttributeCount(); i++) { - if (xpp.getAttributeName(i).equals(attrName)) { - return i; - } - } - return -1; - } - - public VectorMasterDrawable(XmlPullParser parser) { - this.xpp = parser; - buildVectorModel(); - } - - @NonNull - public static VectorMasterDrawable fromXMLFile(File file) throws XmlPullParserException { - final var source = FileIOUtils.readFile2String(file); - return VectorMasterDrawable.fromXML(source); - } - - @NonNull - @Contract("_ -> new") - public static VectorMasterDrawable fromXML(@NonNull String vectorXML) - throws XmlPullParserException { - final var factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - - final var parser = factory.newPullParser(); - parser.setInput(new StringReader(vectorXML)); - return new VectorMasterDrawable(parser); - } - - public int getResID() { - return resID; - } - - public void setResID(int resID) { - this.resID = resID; - buildVectorModel(); - scaleMatrix = null; - } - - public boolean isUseLegacyParser() { - return useLegacyParser; - } - - public void setUseLegacyParser(boolean useLegacyParser) { - this.useLegacyParser = useLegacyParser; - buildVectorModel(); - scaleMatrix = null; - } - - @Override - public void draw(Canvas canvas) { - - if (vectorModel == null) { - return; - } - - if (scaleMatrix == null) { - int temp1 = Utils.dpToPx((int) vectorModel.getWidth()); - int temp2 = Utils.dpToPx((int) vectorModel.getHeight()); - - setBounds(0, 0, temp1, temp2); - } - - setAlpha(Utils.getAlphaFromFloat(vectorModel.getAlpha())); - - if (left != 0 || top != 0) { - int tempSaveCount = canvas.save(); - canvas.translate(left, top); - vectorModel.drawPaths(canvas, offsetX, offsetY, scaleX, scaleY); - canvas.restoreToCount(tempSaveCount); - } else { - vectorModel.drawPaths(canvas, offsetX, offsetY, scaleX, scaleY); - } - } - - @Override - public void setAlpha(int i) { - vectorModel.setAlpha(Utils.getAlphaFromInt(i)); - } - - @Override - public void setColorFilter(ColorFilter colorFilter) {} - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - - if (bounds.width() != 0 && bounds.height() != 0) { - - left = bounds.left; - top = bounds.top; - - width = bounds.width(); - height = bounds.height(); - - buildScaleMatrix(); - scaleAllPaths(); - scaleAllStrokes(); - } - } - - @Override - public int getIntrinsicWidth() { - return Utils.dpToPx((int) vectorModel.getWidth()); - } - - @Override - public int getIntrinsicHeight() { - return Utils.dpToPx((int) vectorModel.getHeight()); - } - - private void buildScaleMatrix() { - scaleMatrix = new Matrix(); - - scaleMatrix.postTranslate( - width / 2 - vectorModel.getViewportWidth() / 2, - height / 2 - vectorModel.getViewportHeight() / 2); - - float widthRatio = width / vectorModel.getViewportWidth(); - float heightRatio = height / vectorModel.getViewportHeight(); - float ratio = Math.min(widthRatio, heightRatio); - - scaleRatio = ratio; - - scaleMatrix.postScale(ratio, ratio, width / 2, height / 2); - } - - private void scaleAllPaths() { - vectorModel.scaleAllPaths(scaleMatrix); - } - - private void scaleAllStrokes() { - strokeRatio = Math.min(width / vectorModel.getWidth(), height / vectorModel.getHeight()); - vectorModel.scaleAllStrokeWidth(strokeRatio); - } - - public Path getFullPath() { - if (vectorModel != null) { - return vectorModel.getFullpath(); - } - return null; - } - - public GroupModel getGroupModelByName(String name) { - GroupModel gModel; - for (GroupModel groupModel : vectorModel.getGroupModels()) { - if (Utils.isEqual(groupModel.getName(), name)) { - return groupModel; - } else { - gModel = groupModel.getGroupModelByName(name); - if (gModel != null) return gModel; - } - } - return null; - } - - public PathModel getPathModelByName(String name) { - PathModel pModel = null; - for (PathModel pathModel : vectorModel.getPathModels()) { - if (Utils.isEqual(pathModel.getName(), name)) { - return pathModel; - } - } - for (GroupModel groupModel : vectorModel.getGroupModels()) { - pModel = groupModel.getPathModelByName(name); - if (pModel != null && Utils.isEqual(pModel.getName(), name)) return pModel; - } - return pModel; - } - - public ClipPathModel getClipPathModelByName(String name) { - ClipPathModel cModel = null; - for (ClipPathModel clipPathModel : vectorModel.getClipPathModels()) { - if (Utils.isEqual(clipPathModel.getName(), name)) { - return clipPathModel; - } - } - for (GroupModel groupModel : vectorModel.getGroupModels()) { - cModel = groupModel.getClipPathModelByName(name); - if (cModel != null && Utils.isEqual(cModel.getName(), name)) return cModel; - } - return cModel; - } - - public void update() { - invalidateSelf(); - } - - public float getScaleRatio() { - return scaleRatio; - } - - public float getStrokeRatio() { - return strokeRatio; - } - - public Matrix getScaleMatrix() { - return scaleMatrix; - } - - public float getOffsetX() { - return offsetX; - } - - public void setOffsetX(float offsetX) { - this.offsetX = offsetX; - invalidateSelf(); - } - - public float getOffsetY() { - return offsetY; - } - - public void setOffsetY(float offsetY) { - this.offsetY = offsetY; - invalidateSelf(); - } - - public float getScaleX() { - return scaleX; - } - - public void setScaleX(float scaleX) { - this.scaleX = scaleX; - invalidateSelf(); - } - - public float getScaleY() { - return scaleY; - } - - public void setScaleY(float scaleY) { - this.scaleY = scaleY; - invalidateSelf(); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterView.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterView.java deleted file mode 100644 index 1d8e42daf..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/VectorMasterView.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.sdsmdg.harjot.vectormaster; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.View; - -import com.itsaky.androidide.resources.R; -import com.sdsmdg.harjot.vectormaster.models.ClipPathModel; -import com.sdsmdg.harjot.vectormaster.models.GroupModel; -import com.sdsmdg.harjot.vectormaster.models.PathModel; -import com.sdsmdg.harjot.vectormaster.models.VectorModel; -import com.sdsmdg.harjot.vectormaster.utilities.Utils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.util.Stack; - -public class VectorMasterView extends View { - - private Matrix scaleMatrix; - private float scaleRatio, strokeRatio; - VectorModel vectorModel; - Context context; - Resources resources; - int resID = -1; - boolean useLegacyParser = true; - XmlPullParser xpp; - int width = 0, height = 0; - - public VectorMasterView(Context context) { - super(context); - this.context = context; - } - - public VectorMasterView(Context context, AttributeSet attrs) { - super(context, attrs); - this.context = context; - init(attrs); - } - - void init(AttributeSet attrs) { - resources = context.getResources(); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VectorMasterView); - final int N = a.getIndexCount(); - for (int i = 0; i < N; ++i) { - int attr = a.getIndex(i); - if (attr == R.styleable.VectorMasterView_vector_src) { - resID = a.getResourceId(attr, -1); - } else if (attr == R.styleable.VectorMasterView_use_legacy_parser) { - useLegacyParser = a.getBoolean(attr, false); - } - } - a.recycle(); - - buildVectorModel(); - } - - void buildVectorModel() { - - if (resID == -1) { - vectorModel = null; - return; - } - - xpp = resources.getXml(resID); - - int tempPosition; - PathModel pathModel = new PathModel(); - vectorModel = new VectorModel(); - GroupModel groupModel = new GroupModel(); - ClipPathModel clipPathModel = new ClipPathModel(); - Stack groupModelStack = new Stack<>(); - - try { - int event = xpp.getEventType(); - while (event != XmlPullParser.END_DOCUMENT) { - String name = xpp.getName(); - switch (event) { - case XmlPullParser.START_TAG: - if (name.equals("vector")) { - tempPosition = getAttrPosition(xpp, "viewportWidth"); - vectorModel.setViewportWidth( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_VIEWPORT_WIDTH); - - tempPosition = getAttrPosition(xpp, "viewportHeight"); - vectorModel.setViewportHeight( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_VIEWPORT_HEIGHT); - - tempPosition = getAttrPosition(xpp, "alpha"); - vectorModel.setAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_ALPHA); - - tempPosition = getAttrPosition(xpp, "name"); - vectorModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "width"); - vectorModel.setWidth( - (tempPosition != -1) - ? Utils.getFloatFromDimensionString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_WIDTH); - - tempPosition = getAttrPosition(xpp, "height"); - vectorModel.setHeight( - (tempPosition != -1) - ? Utils.getFloatFromDimensionString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.VECTOR_HEIGHT); - } else if (name.equals("path")) { - pathModel = new PathModel(); - - tempPosition = getAttrPosition(xpp, "name"); - pathModel.setName((tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "fillAlpha"); - pathModel.setFillAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_ALPHA); - - tempPosition = getAttrPosition(xpp, "fillColor"); - pathModel.setFillColor( - (tempPosition != -1) - ? Utils.getColorFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_COLOR); - - tempPosition = getAttrPosition(xpp, "fillType"); - pathModel.setFillType( - (tempPosition != -1) - ? Utils.getFillTypeFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_FILL_TYPE); - - tempPosition = getAttrPosition(xpp, "pathData"); - pathModel.setPathData( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "strokeAlpha"); - pathModel.setStrokeAlpha( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_ALPHA); - - tempPosition = getAttrPosition(xpp, "strokeColor"); - pathModel.setStrokeColor( - (tempPosition != -1) - ? Utils.getColorFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_COLOR); - - tempPosition = getAttrPosition(xpp, "strokeLineCap"); - pathModel.setStrokeLineCap( - (tempPosition != -1) - ? Utils.getLineCapFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_LINE_CAP); - - tempPosition = getAttrPosition(xpp, "strokeLineJoin"); - pathModel.setStrokeLineJoin( - (tempPosition != -1) - ? Utils.getLineJoinFromString(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_LINE_JOIN); - - tempPosition = getAttrPosition(xpp, "strokeMiterLimit"); - pathModel.setStrokeMiterLimit( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_MITER_LIMIT); - - tempPosition = getAttrPosition(xpp, "strokeWidth"); - pathModel.setStrokeWidth( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_STROKE_WIDTH); - - tempPosition = getAttrPosition(xpp, "trimPathEnd"); - pathModel.setTrimPathEnd( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_END); - - tempPosition = getAttrPosition(xpp, "trimPathOffset"); - pathModel.setTrimPathOffset( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_OFFSET); - - tempPosition = getAttrPosition(xpp, "trimPathStart"); - pathModel.setTrimPathStart( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.PATH_TRIM_PATH_START); - - pathModel.buildPath(useLegacyParser); - } else if (name.equals("group")) { - groupModel = new GroupModel(); - - tempPosition = getAttrPosition(xpp, "name"); - groupModel.setName((tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "pivotX"); - groupModel.setPivotX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_PIVOT_X); - - tempPosition = getAttrPosition(xpp, "pivotY"); - groupModel.setPivotY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_PIVOT_Y); - - tempPosition = getAttrPosition(xpp, "rotation"); - groupModel.setRotation( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_ROTATION); - - tempPosition = getAttrPosition(xpp, "scaleX"); - groupModel.setScaleX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_SCALE_X); - - tempPosition = getAttrPosition(xpp, "scaleY"); - groupModel.setScaleY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_SCALE_Y); - - tempPosition = getAttrPosition(xpp, "translateX"); - groupModel.setTranslateX( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_TRANSLATE_X); - - tempPosition = getAttrPosition(xpp, "translateY"); - groupModel.setTranslateY( - (tempPosition != -1) - ? Float.parseFloat(xpp.getAttributeValue(tempPosition)) - : DefaultValues.GROUP_TRANSLATE_Y); - - groupModelStack.push(groupModel); - } else if (name.equals("clip-path")) { - clipPathModel = new ClipPathModel(); - - tempPosition = getAttrPosition(xpp, "name"); - clipPathModel.setName( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - tempPosition = getAttrPosition(xpp, "pathData"); - clipPathModel.setPathData( - (tempPosition != -1) ? xpp.getAttributeValue(tempPosition) : null); - - clipPathModel.buildPath(useLegacyParser); - } - break; - - case XmlPullParser.END_TAG: - if (name.equals("path")) { - if (groupModelStack.size() == 0) { - vectorModel.addPathModel(pathModel); - } else { - groupModelStack.peek().addPathModel(pathModel); - } - vectorModel.getFullpath().addPath(pathModel.getPath()); - } else if (name.equals("clip-path")) { - if (groupModelStack.size() == 0) { - vectorModel.addClipPathModel(clipPathModel); - } else { - groupModelStack.peek().addClipPathModel(clipPathModel); - } - } else if (name.equals("group")) { - GroupModel topGroupModel = groupModelStack.pop(); - if (groupModelStack.size() == 0) { - topGroupModel.setParent(null); - vectorModel.addGroupModel(topGroupModel); - } else { - topGroupModel.setParent(groupModelStack.peek()); - groupModelStack.peek().addGroupModel(topGroupModel); - } - } else if (name.equals("vector")) { - vectorModel.buildTransformMatrices(); - } - break; - } - event = xpp.next(); - } - } catch (XmlPullParserException | IOException e) { - e.printStackTrace(); - } - } - - int getAttrPosition(XmlPullParser xpp, String attrName) { - for (int i = 0; i < xpp.getAttributeCount(); i++) { - if (xpp.getAttributeName(i).equals(attrName)) { - return i; - } - } - return -1; - } - - public VectorMasterView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - this.context = context; - init(attrs); - } - - public int getResID() { - return resID; - } - - public void setResID(int resID) { - this.resID = resID; - } - - public Path getFullPath() { - if (vectorModel != null) { - return vectorModel.getFullpath(); - } - return null; - } - - public GroupModel getGroupModelByName(String name) { - GroupModel gModel; - for (GroupModel groupModel : vectorModel.getGroupModels()) { - if (Utils.isEqual(groupModel.getName(), name)) { - return groupModel; - } else { - gModel = groupModel.getGroupModelByName(name); - if (gModel != null) return gModel; - } - } - return null; - } - - public PathModel getPathModelByName(String name) { - PathModel pModel = null; - for (PathModel pathModel : vectorModel.getPathModels()) { - if (Utils.isEqual(pathModel.getName(), name)) { - return pathModel; - } - } - for (GroupModel groupModel : vectorModel.getGroupModels()) { - pModel = groupModel.getPathModelByName(name); - if (pModel != null && Utils.isEqual(pModel.getName(), name)) return pModel; - } - return pModel; - } - - public ClipPathModel getClipPathModelByName(String name) { - ClipPathModel cModel = null; - for (ClipPathModel clipPathModel : vectorModel.getClipPathModels()) { - if (Utils.isEqual(clipPathModel.getName(), name)) { - return clipPathModel; - } - } - for (GroupModel groupModel : vectorModel.getGroupModels()) { - cModel = groupModel.getClipPathModelByName(name); - if (cModel != null && Utils.isEqual(cModel.getName(), name)) return cModel; - } - return cModel; - } - - public void update() { - invalidate(); - } - - public float getScaleRatio() { - return scaleRatio; - } - - public float getStrokeRatio() { - return strokeRatio; - } - - public Matrix getScaleMatrix() { - return scaleMatrix; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - if (w != 0 && h != 0) { - width = w; - height = h; - - buildScaleMatrix(); - scaleAllPaths(); - scaleAllStrokes(); - } - } - - @SuppressLint("CanvasSize") - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - width = canvas.getWidth(); - height = canvas.getHeight(); - - if (vectorModel == null) { - return; - } - - setAlpha(vectorModel.getAlpha()); - - vectorModel.drawPaths(canvas); - } - - void buildScaleMatrix() { - - scaleMatrix = new Matrix(); - - scaleMatrix.postTranslate( - width / 2 - vectorModel.getViewportWidth() / 2, - height / 2 - vectorModel.getViewportHeight() / 2); - - float widthRatio = width / vectorModel.getViewportWidth(); - float heightRatio = height / vectorModel.getViewportHeight(); - float ratio = Math.min(widthRatio, heightRatio); - - scaleRatio = ratio; - - scaleMatrix.postScale(ratio, ratio, width / 2, height / 2); - } - - void scaleAllPaths() { - vectorModel.scaleAllPaths(scaleMatrix); - } - - void scaleAllStrokes() { - strokeRatio = Math.min(width / vectorModel.getWidth(), height / vectorModel.getHeight()); - vectorModel.scaleAllStrokeWidth(strokeRatio); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/enums/TintMode.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/enums/TintMode.java deleted file mode 100644 index 4742c74ca..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/enums/TintMode.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.enums; - -/** Created by Harjot on 20-Jun-17. */ -public enum TintMode { - ADD, - MULTIPLY, - SCREEN, - SRC_ATOP, - SCR_IN, - SRC_OVER -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/ClipPathModel.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/ClipPathModel.java deleted file mode 100644 index c2eedb868..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/ClipPathModel.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.models; - -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.RectF; - -import com.sdsmdg.harjot.vectormaster.utilities.parser.PathParser; - -public class ClipPathModel { - private String name; - private String pathData; - - private Path originalPath; - private Path path; - - private Paint clipPaint; - - public ClipPathModel() { - path = new Path(); - - clipPaint = new Paint(); - clipPaint.setAntiAlias(true); - clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - } - - public void buildPath(boolean useLegacyParser) { - if (useLegacyParser) { - originalPath = - com.sdsmdg.harjot.vectormaster.utilities.legacyparser.PathParser.createPathFromPathData( - pathData); - } else { - originalPath = PathParser.doPath(pathData); - } - - path = new Path(originalPath); - } - - public void transform(Matrix matrix) { - path = new Path(originalPath); - - path.transform(matrix); - } - - public Paint getClipPaint() { - return clipPaint; - } - - public void setClipPaint(Paint clipPaint) { - this.clipPaint = clipPaint; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPathData() { - return pathData; - } - - public void setPathData(String pathData) { - this.pathData = pathData; - } - - public Path getPath() { - return path; - } - - public void setPath(Path path) { - this.path = path; - } - - public Path getScaledAndOffsetPath(float offsetX, float offsetY, float scaleX, float scaleY) { - Path newPath = new Path(path); - newPath.offset(offsetX, offsetY); - newPath.transform(getScaleMatrix(newPath, scaleX, scaleY)); - return newPath; - } - - public Matrix getScaleMatrix(Path srcPath, float scaleX, float scaleY) { - Matrix scaleMatrix = new Matrix(); - RectF rectF = new RectF(); - srcPath.computeBounds(rectF, true); - scaleMatrix.setScale(scaleX, scaleY, rectF.left, rectF.top); - return scaleMatrix; - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/GroupModel.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/GroupModel.java deleted file mode 100644 index 1441491fc..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/GroupModel.java +++ /dev/null @@ -1,280 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.models; - -import android.graphics.Canvas; -import android.graphics.Matrix; - -import com.sdsmdg.harjot.vectormaster.DefaultValues; -import com.sdsmdg.harjot.vectormaster.utilities.Utils; - -import java.util.ArrayList; - -public class GroupModel { - private String name; - - private float rotation; - private float pivotX, pivotY; - private float scaleX, scaleY; - private float translateX, translateY; - - private Matrix scaleMatrix; - private Matrix originalTransformMatrix; - private Matrix finalTransformMatrix; - private GroupModel parent; - - private ArrayList groupModels; - private ArrayList pathModels; - private ArrayList clipPathModels; - - public GroupModel() { - rotation = DefaultValues.GROUP_ROTATION; - pivotX = DefaultValues.GROUP_PIVOT_X; - pivotY = DefaultValues.GROUP_PIVOT_Y; - scaleX = DefaultValues.GROUP_SCALE_X; - scaleY = DefaultValues.GROUP_SCALE_Y; - translateX = DefaultValues.GROUP_TRANSLATE_X; - translateY = DefaultValues.GROUP_TRANSLATE_Y; - - groupModels = new ArrayList<>(); - pathModels = new ArrayList<>(); - clipPathModels = new ArrayList<>(); - } - - public void drawPaths(Canvas canvas, float offsetX, float offsetY, float scaleX, float scaleY) { - for (ClipPathModel clipPathModel : clipPathModels) { - canvas.clipPath(clipPathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY)); - } - for (GroupModel groupModel : groupModels) { - groupModel.drawPaths(canvas, offsetX, offsetY, scaleX, scaleY); - } - for (PathModel pathModel : pathModels) { - if (pathModel.isFillAndStroke()) { - pathModel.makeFillPaint(); - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - pathModel.makeStrokePaint(); - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - } else { - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - } - } - } - - public void drawPaths(Canvas canvas) { - for (ClipPathModel clipPathModel : clipPathModels) { - canvas.clipPath(clipPathModel.getPath()); - } - for (GroupModel groupModel : groupModels) { - groupModel.drawPaths(canvas); - } - for (PathModel pathModel : pathModels) { - if (pathModel.isFillAndStroke()) { - pathModel.makeFillPaint(); - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - pathModel.makeStrokePaint(); - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - } else { - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - } - } - } - - public void scaleAllPaths(Matrix scaleMatrix) { - this.scaleMatrix = scaleMatrix; - finalTransformMatrix = new Matrix(originalTransformMatrix); - finalTransformMatrix.postConcat(scaleMatrix); - for (GroupModel groupModel : groupModels) { - groupModel.scaleAllPaths(scaleMatrix); - } - for (PathModel pathModel : pathModels) { - pathModel.transform(finalTransformMatrix); - } - for (ClipPathModel clipPathModel : clipPathModels) { - clipPathModel.transform(finalTransformMatrix); - } - } - - public void scaleAllStrokeWidth(float ratio) { - for (GroupModel groupModel : groupModels) { - groupModel.scaleAllStrokeWidth(ratio); - } - for (PathModel pathModel : pathModels) { - pathModel.setStrokeRatio(ratio); - } - } - - public void buildTransformMatrix() { - - originalTransformMatrix = new Matrix(); - - originalTransformMatrix.postScale(scaleX, scaleY, pivotX, pivotY); - originalTransformMatrix.postRotate(rotation, pivotX, pivotY); - originalTransformMatrix.postTranslate(translateX, translateY); - - if (parent != null) { - originalTransformMatrix.postConcat(parent.getOriginalTransformMatrix()); - } - - for (GroupModel groupModel : groupModels) { - groupModel.buildTransformMatrix(); - } - } - - public void updateAndScalePaths() { - if (scaleMatrix != null) { - buildTransformMatrix(); - scaleAllPaths(scaleMatrix); - } - } - - public GroupModel getGroupModelByName(String name) { - GroupModel grpModel = null; - for (GroupModel groupModel : groupModels) { - if (Utils.isEqual(groupModel.getName(), name)) { - grpModel = groupModel; - return grpModel; - } else { - grpModel = groupModel.getGroupModelByName(name); - if (grpModel != null) return grpModel; - } - } - return grpModel; - } - - public PathModel getPathModelByName(String name) { - PathModel pModel = null; - for (PathModel pathModel : pathModels) { - if (Utils.isEqual(pathModel.getName(), name)) { - return pathModel; - } - } - for (GroupModel groupModel : groupModels) { - pModel = groupModel.getPathModelByName(name); - if (pModel != null && Utils.isEqual(pModel.getName(), name)) return pModel; - } - return pModel; - } - - public ClipPathModel getClipPathModelByName(String name) { - ClipPathModel cModel = null; - for (ClipPathModel clipPathModel : getClipPathModels()) { - if (Utils.isEqual(clipPathModel.getName(), name)) { - return clipPathModel; - } - } - for (GroupModel groupModel : getGroupModels()) { - cModel = groupModel.getClipPathModelByName(name); - if (cModel != null && Utils.isEqual(cModel.getName(), name)) return cModel; - } - return cModel; - } - - public Matrix getOriginalTransformMatrix() { - return originalTransformMatrix; - } - - public GroupModel getParent() { - return parent; - } - - public void setParent(GroupModel parent) { - this.parent = parent; - } - - public void addGroupModel(GroupModel groupModel) { - groupModels.add(groupModel); - } - - public ArrayList getGroupModels() { - return groupModels; - } - - public void addPathModel(PathModel pathModel) { - pathModels.add(pathModel); - } - - public ArrayList getPathModels() { - return pathModels; - } - - public void addClipPathModel(ClipPathModel clipPathModel) { - clipPathModels.add(clipPathModel); - } - - public ArrayList getClipPathModels() { - return clipPathModels; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public float getRotation() { - return rotation; - } - - public void setRotation(float rotation) { - this.rotation = rotation; - updateAndScalePaths(); - } - - public float getPivotX() { - return pivotX; - } - - public void setPivotX(float pivotX) { - this.pivotX = pivotX; - } - - public float getPivotY() { - return pivotY; - } - - public void setPivotY(float pivotY) { - this.pivotY = pivotY; - } - - public float getScaleX() { - return scaleX; - } - - public void setScaleX(float scaleX) { - this.scaleX = scaleX; - updateAndScalePaths(); - } - - public float getScaleY() { - return scaleY; - } - - public void setScaleY(float scaleY) { - this.scaleY = scaleY; - updateAndScalePaths(); - } - - public float getTranslateX() { - return translateX; - } - - public void setTranslateX(float translateX) { - this.translateX = translateX; - updateAndScalePaths(); - } - - public float getTranslateY() { - return translateY; - } - - public void setTranslateY(float translateY) { - this.translateY = translateY; - updateAndScalePaths(); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/PathModel.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/PathModel.java deleted file mode 100644 index d4ac4f252..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/PathModel.java +++ /dev/null @@ -1,317 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.models; - -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.RectF; - -import com.sdsmdg.harjot.vectormaster.DefaultValues; -import com.sdsmdg.harjot.vectormaster.utilities.Utils; -import com.sdsmdg.harjot.vectormaster.utilities.parser.PathParser; - -public class PathModel { - - private String name; - - private float fillAlpha; - private int fillColor; - - private Path.FillType fillType; - - private String pathData; - - private float trimPathStart, trimPathEnd, trimPathOffset; - - private float strokeAlpha; - private int strokeColor; - private Paint.Cap strokeLineCap; - private Paint.Join strokeLineJoin; - private float strokeMiterLimit; - private float strokeWidth; - - private float strokeRatio; - - private boolean isFillAndStroke = false; - - // Support for trim-paths is not available - - private Path originalPath; - private Path path; - private Path trimmedPath; - private Paint pathPaint; - - private Matrix scaleMatrix; - - public PathModel() { - fillAlpha = DefaultValues.PATH_FILL_ALPHA; - fillColor = DefaultValues.PATH_FILL_COLOR; - fillType = DefaultValues.PATH_FILL_TYPE; - trimPathStart = DefaultValues.PATH_TRIM_PATH_START; - trimPathEnd = DefaultValues.PATH_TRIM_PATH_END; - trimPathOffset = DefaultValues.PATH_TRIM_PATH_OFFSET; - strokeAlpha = DefaultValues.PATH_STROKE_ALPHA; - strokeColor = DefaultValues.PATH_STROKE_COLOR; - strokeLineCap = DefaultValues.PATH_STROKE_LINE_CAP; - strokeLineJoin = DefaultValues.PATH_STROKE_LINE_JOIN; - strokeMiterLimit = DefaultValues.PATH_STROKE_MITER_LIMIT; - strokeWidth = DefaultValues.PATH_STROKE_WIDTH; - strokeRatio = DefaultValues.PATH_STROKE_RATIO; - - pathPaint = new Paint(); - pathPaint.setAntiAlias(true); - updatePaint(); - } - - public void updatePaint() { - pathPaint.setStrokeWidth(strokeWidth * strokeRatio); - - if (fillColor != Color.TRANSPARENT && strokeColor != Color.TRANSPARENT) { - isFillAndStroke = true; - } else if (fillColor != Color.TRANSPARENT) { - pathPaint.setColor(fillColor); - pathPaint.setAlpha(Utils.getAlphaFromFloat(fillAlpha)); - pathPaint.setStyle(Paint.Style.FILL); - isFillAndStroke = false; - } else if (strokeColor != Color.TRANSPARENT) { - pathPaint.setColor(strokeColor); - pathPaint.setAlpha(Utils.getAlphaFromFloat(strokeAlpha)); - pathPaint.setStyle(Paint.Style.STROKE); - isFillAndStroke = false; - } else { - pathPaint.setColor(Color.TRANSPARENT); - } - - pathPaint.setStrokeCap(strokeLineCap); - pathPaint.setStrokeJoin(strokeLineJoin); - pathPaint.setStrokeMiter(strokeMiterLimit); - } - - public void buildPath(boolean useLegacyParser) { - if (useLegacyParser) { - originalPath = - com.sdsmdg.harjot.vectormaster.utilities.legacyparser.PathParser.createPathFromPathData( - pathData); - } else { - originalPath = PathParser.doPath(pathData); - } - if (originalPath != null) originalPath.setFillType(fillType); - - path = new Path(originalPath); - } - - public void makeStrokePaint() { - pathPaint.setColor(strokeColor); - pathPaint.setAlpha(Utils.getAlphaFromFloat(strokeAlpha)); - pathPaint.setStyle(Paint.Style.STROKE); - } - - public void makeFillPaint() { - pathPaint.setColor(fillColor); - pathPaint.setAlpha(Utils.getAlphaFromFloat(fillAlpha)); - pathPaint.setStyle(Paint.Style.FILL); - } - - public void transform(Matrix matrix) { - scaleMatrix = matrix; - - trimPath(); - } - - public void trimPath() { - if (scaleMatrix != null) { - if (trimPathStart == 0 && trimPathEnd == 1 && trimPathOffset == 0) { - path = new Path(originalPath); - path.transform(scaleMatrix); - } else { - PathMeasure pathMeasure = new PathMeasure(originalPath, false); - float length = pathMeasure.getLength(); - trimmedPath = new Path(); - pathMeasure.getSegment( - (trimPathStart + trimPathOffset) * length, - (trimPathEnd + trimPathOffset) * length, - trimmedPath, - true); - path = new Path(trimmedPath); - path.transform(scaleMatrix); - } - } - } - - public Path getTrimmedPath() { - return trimmedPath; - } - - public void setTrimmedPath(Path trimmedPath) { - this.trimmedPath = trimmedPath; - } - - public Path getPath() { - return path; - } - - public void setPath(Path path) { - this.path = path; - } - - public Path getScaledAndOffsetPath(float offsetX, float offsetY, float scaleX, float scaleY) { - Path newPath = new Path(path); - newPath.offset(offsetX, offsetY); - newPath.transform(getScaleMatrix(newPath, scaleX, scaleY)); - return newPath; - } - - public Matrix getScaleMatrix(Path srcPath, float scaleX, float scaleY) { - Matrix scaleMatrix = new Matrix(); - RectF rectF = new RectF(); - srcPath.computeBounds(rectF, true); - scaleMatrix.setScale(scaleX, scaleY, rectF.left, rectF.top); - return scaleMatrix; - } - - public Paint getPathPaint() { - return pathPaint; - } - - public void setPathPaint(Paint pathPaint) { - this.pathPaint = pathPaint; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public float getFillAlpha() { - return fillAlpha; - } - - public void setFillAlpha(float fillAlpha) { - this.fillAlpha = fillAlpha; - updatePaint(); - } - - public int getFillColor() { - return fillColor; - } - - public void setFillColor(int fillColor) { - this.fillColor = fillColor; - updatePaint(); - } - - public Path.FillType getFillType() { - return fillType; - } - - public void setFillType(Path.FillType fillType) { - this.fillType = fillType; - if (originalPath != null) originalPath.setFillType(fillType); - } - - public String getPathData() { - return pathData; - } - - public void setPathData(String pathData) { - this.pathData = pathData; - } - - public float getTrimPathStart() { - return trimPathStart; - } - - public void setTrimPathStart(float trimPathStart) { - this.trimPathStart = trimPathStart; - trimPath(); - } - - public float getTrimPathEnd() { - return trimPathEnd; - } - - public void setTrimPathEnd(float trimPathEnd) { - this.trimPathEnd = trimPathEnd; - trimPath(); - } - - public float getTrimPathOffset() { - return trimPathOffset; - } - - public void setTrimPathOffset(float trimPathOffset) { - this.trimPathOffset = trimPathOffset; - trimPath(); - } - - public float getStrokeAlpha() { - return strokeAlpha; - } - - public void setStrokeAlpha(float strokeAlpha) { - this.strokeAlpha = strokeAlpha; - updatePaint(); - } - - public int getStrokeColor() { - return strokeColor; - } - - public void setStrokeColor(int strokeColor) { - this.strokeColor = strokeColor; - updatePaint(); - } - - public Paint.Cap getStrokeLineCap() { - return strokeLineCap; - } - - public void setStrokeLineCap(Paint.Cap strokeLineCap) { - this.strokeLineCap = strokeLineCap; - updatePaint(); - } - - public Paint.Join getStrokeLineJoin() { - return strokeLineJoin; - } - - public void setStrokeLineJoin(Paint.Join strokeLineJoin) { - this.strokeLineJoin = strokeLineJoin; - updatePaint(); - } - - public float getStrokeMiterLimit() { - return strokeMiterLimit; - } - - public void setStrokeMiterLimit(float strokeMiterLimit) { - this.strokeMiterLimit = strokeMiterLimit; - updatePaint(); - } - - public float getStrokeWidth() { - return strokeWidth; - } - - public void setStrokeWidth(float strokeWidth) { - this.strokeWidth = strokeWidth; - updatePaint(); - } - - public float getStrokeRatio() { - return strokeRatio; - } - - public void setStrokeRatio(float strokeRatio) { - this.strokeRatio = strokeRatio; - updatePaint(); - } - - public boolean isFillAndStroke() { - return isFillAndStroke; - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/VectorModel.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/VectorModel.java deleted file mode 100644 index d7055403d..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/models/VectorModel.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.models; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Path; - -import com.sdsmdg.harjot.vectormaster.enums.TintMode; - -import java.util.ArrayList; - -public class VectorModel { - - private String name; - - private float width, height; - - private float alpha = 1.0f; - - private boolean autoMirrored = false; - - private int tint = Color.TRANSPARENT; - private TintMode tintMode = TintMode.SCR_IN; - - private float viewportWidth, viewportHeight; - - private ArrayList groupModels; - private ArrayList pathModels; - private ArrayList clipPathModels; - - private Path fullpath; - - private Matrix scaleMatrix; - - public VectorModel() { - groupModels = new ArrayList<>(); - pathModels = new ArrayList<>(); - clipPathModels = new ArrayList<>(); - fullpath = new Path(); - } - - public void drawPaths(Canvas canvas, float offsetX, float offsetY, float scaleX, float scaleY) { - for (ClipPathModel clipPathModel : clipPathModels) { - canvas.clipPath(clipPathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY)); - } - for (GroupModel groupModel : groupModels) { - groupModel.drawPaths(canvas, offsetX, offsetY, scaleX, scaleY); - } - for (PathModel pathModel : pathModels) { - if (pathModel.isFillAndStroke()) { - pathModel.makeFillPaint(); - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - pathModel.makeStrokePaint(); - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - } else { - canvas.drawPath( - pathModel.getScaledAndOffsetPath(offsetX, offsetY, scaleX, scaleY), - pathModel.getPathPaint()); - } - } - } - - public void drawPaths(Canvas canvas) { - for (ClipPathModel clipPathModel : clipPathModels) { - canvas.clipPath(clipPathModel.getPath()); - } - for (GroupModel groupModel : groupModels) { - groupModel.drawPaths(canvas); - } - for (PathModel pathModel : pathModels) { - if (pathModel.isFillAndStroke()) { - pathModel.makeFillPaint(); - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - pathModel.makeStrokePaint(); - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - } else { - canvas.drawPath(pathModel.getPath(), pathModel.getPathPaint()); - } - } - } - - public void scaleAllPaths(Matrix scaleMatrix) { - this.scaleMatrix = scaleMatrix; - for (GroupModel groupModel : groupModels) { - groupModel.scaleAllPaths(scaleMatrix); - } - for (PathModel pathModel : pathModels) { - pathModel.transform(scaleMatrix); - } - for (ClipPathModel clipPathModel : clipPathModels) { - clipPathModel.transform(scaleMatrix); - } - } - - public void scaleAllStrokeWidth(float ratio) { - for (GroupModel groupModel : groupModels) { - groupModel.scaleAllStrokeWidth(ratio); - } - for (PathModel pathModel : pathModels) { - pathModel.setStrokeRatio(ratio); - } - } - - public void buildTransformMatrices() { - for (GroupModel groupModel : groupModels) { - groupModel.buildTransformMatrix(); - } - } - - public void addGroupModel(GroupModel groupModel) { - groupModels.add(groupModel); - } - - public ArrayList getGroupModels() { - return groupModels; - } - - public void addPathModel(PathModel pathModel) { - pathModels.add(pathModel); - } - - public ArrayList getPathModels() { - return pathModels; - } - - public void addClipPathModel(ClipPathModel clipPathModel) { - clipPathModels.add(clipPathModel); - } - - public ArrayList getClipPathModels() { - return clipPathModels; - } - - public Path getFullpath() { - return fullpath; - } - - public void setFullpath(Path fullpath) { - this.fullpath = fullpath; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public float getWidth() { - return width; - } - - public void setWidth(float width) { - this.width = width; - } - - public float getHeight() { - return height; - } - - public void setHeight(float height) { - this.height = height; - } - - public float getAlpha() { - return alpha; - } - - public void setAlpha(float alpha) { - this.alpha = alpha; - } - - public boolean isAutoMirrored() { - return autoMirrored; - } - - public void setAutoMirrored(boolean autoMirrored) { - this.autoMirrored = autoMirrored; - } - - public int getTint() { - return tint; - } - - public void setTint(int tint) { - this.tint = tint; - } - - public TintMode getTintMode() { - return tintMode; - } - - public void setTintMode(TintMode tintMode) { - this.tintMode = tintMode; - } - - public float getViewportWidth() { - return viewportWidth; - } - - public void setViewportWidth(float viewportWidth) { - this.viewportWidth = viewportWidth; - } - - public float getViewportHeight() { - return viewportHeight; - } - - public void setViewportHeight(float viewportHeight) { - this.viewportHeight = viewportHeight; - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/Utils.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/Utils.java deleted file mode 100644 index d8db0a418..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/Utils.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.utilities; - -import android.content.res.Resources; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; - -public class Utils { - - public static int getColorFromString(String value) { - int color = Color.TRANSPARENT; - - if (value.length() == 4) { - color = - Color.parseColor( - "#" - + value.charAt(1) - + value.charAt(1) - + value.charAt(2) - + value.charAt(2) - + value.charAt(3) - + value.charAt(3)); - } else if (value.length() == 7 || value.length() == 9) { - color = Color.parseColor(value); - } else if (value.length() == 2) { - color = - Color.parseColor( - "#" - + value.charAt(1) - + value.charAt(1) - + value.charAt(1) - + value.charAt(1) - + value.charAt(1) - + value.charAt(1) - + value.charAt(1) - + value.charAt(1)); - } - - return color; - } - - public static Path.FillType getFillTypeFromString(String value) { - Path.FillType fillType = Path.FillType.WINDING; - if (value.equals("1")) { - fillType = Path.FillType.EVEN_ODD; - } - return fillType; - } - - public static Paint.Cap getLineCapFromString(String value) { - switch (value) { - case "0": - return Paint.Cap.BUTT; - case "1": - return Paint.Cap.ROUND; - case "2": - return Paint.Cap.SQUARE; - default: - return Paint.Cap.BUTT; - } - } - - public static Paint.Join getLineJoinFromString(String value) { - switch (value) { - case "0": - return Paint.Join.MITER; - case "1": - return Paint.Join.ROUND; - case "2": - return Paint.Join.BEVEL; - default: - return Paint.Join.MITER; - } - } - - public static int getAlphaFromFloat(float value) { - int newValue = (int) (255 * value); - return Math.min(255, newValue); - } - - public static float getAlphaFromInt(int value) { - return (((float) value) / 255.0f); - } - - public static int dpToPx(int dp) { - return (int) (dp * Resources.getSystem().getDisplayMetrics().density); - } - - public static int pxToDp(int px) { - return (int) (px / Resources.getSystem().getDisplayMetrics().density); - } - - public static float getFloatFromDimensionString(String value) { - if (value.contains("dip")) return Float.parseFloat(value.substring(0, value.length() - 3)); - else return Float.parseFloat(value.substring(0, value.length() - 2)); - } - - public static boolean isEqual(Object a, Object b) { - return a == null && b == null || !(a == null || b == null) && a.equals(b); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/legacyparser/PathParser.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/legacyparser/PathParser.java deleted file mode 100644 index 0b66972d4..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/legacyparser/PathParser.java +++ /dev/null @@ -1,755 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ -package com.sdsmdg.harjot.vectormaster.utilities.legacyparser; - -import android.graphics.Path; - -import androidx.annotation.Nullable; - -import com.itsaky.androidide.utils.ILogger; - -import java.util.ArrayList; - -/** This class is a duplicate of the PathParser.java in androidx.core.graphics package. */ -public class PathParser { - - private static final ILogger LOG = ILogger.newInstance("PathParser"); - - // Copy from Arrays.copyOfRange() which is only available from API level 9. - private PathParser() {} - - /** - * @param pathData The string representing a path, the same as "d" string in svg file. - * @return the generated Path object. - */ - public static Path createPathFromPathData(String pathData) { - Path path = new Path(); - PathDataNode[] nodes = createNodesFromPathData(pathData); - if (nodes != null) { - try { - PathDataNode.nodesToPath(nodes, path); - } catch (RuntimeException e) { - throw new RuntimeException("Error in parsing " + pathData, e); - } - return path; - } - return null; - } - /** - * @param pathData The string representing a path, the same as "d" string in svg file. - * @return an array of the PathDataNode. - */ - public static PathDataNode[] createNodesFromPathData(String pathData) { - if (pathData == null) { - return null; - } - int start = 0; - int end = 1; - ArrayList list = new ArrayList(); - while (end < pathData.length()) { - end = nextStart(pathData, end); - String s = pathData.substring(start, end).trim(); - if (s.length() > 0) { - float[] val = getFloats(s); - addNode(list, s.charAt(0), val); - } - start = end; - end++; - } - if ((end - start) == 1 && start < pathData.length()) { - addNode(list, pathData.charAt(start), new float[0]); - } - return list.toArray(new PathDataNode[list.size()]); - } - - private static int nextStart(String s, int end) { - char c; - while (end < s.length()) { - c = s.charAt(end); - // Note that 'e' or 'E' are not valid path commands, but could be - // used for floating point numbers' scientific notation. - // Therefore, when searching for next command, we should ignore 'e' - // and 'E'. - if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0)) && c != 'e' && c != 'E') { - return end; - } - end++; - } - return end; - } - - private static void addNode(ArrayList list, char cmd, float[] val) { - list.add(new PathDataNode(cmd, val)); - } - - /** - * Parse the floats in the string. This is an optimized version of parseFloat(s.split(",|\\s")); - * - * @param s the string containing a command and list of floats - * @return array of floats - */ - private static float[] getFloats(String s) { - if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') { - return new float[0]; - } - try { - float[] results = new float[s.length()]; - int count = 0; - int startPosition = 1; - int endPosition = 0; - ExtractFloatResult result = new ExtractFloatResult(); - int totalLength = s.length(); - // The startPosition should always be the first character of the - // current number, and endPosition is the character after the current - // number. - while (startPosition < totalLength) { - extract(s, startPosition, result); - endPosition = result.mEndPosition; - if (startPosition < endPosition) { - results[count++] = Float.parseFloat(s.substring(startPosition, endPosition)); - } - if (result.mEndWithNegOrDot) { - // Keep the '-' or '.' sign with next number. - startPosition = endPosition; - } else { - startPosition = endPosition + 1; - } - } - return copyOfRange(results, 0, count); - } catch (NumberFormatException e) { - throw new RuntimeException("error in parsing \"" + s + "\"", e); - } - } - - /** - * Copies elements from {@code original} into a new array, from indexes start (inclusive) to end - * (exclusive). The original order of elements is preserved. If {@code end} is greater than {@code - * original.length}, the result is padded with the value {@code 0.0f}. - * - * @param original the original array - * @param start the start index, inclusive - * @param end the end index, exclusive - * @return the new array - * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length} - * @throws IllegalArgumentException if {@code start > end} - * @throws NullPointerException if {@code original == null} - */ - static float[] copyOfRange(float[] original, int start, int end) { - if (start > end) { - throw new IllegalArgumentException(); - } - int originalLength = original.length; - if (start < 0 || start > originalLength) { - throw new ArrayIndexOutOfBoundsException(); - } - int resultLength = end - start; - int copyLength = Math.min(resultLength, originalLength - start); - float[] result = new float[resultLength]; - System.arraycopy(original, start, result, 0, copyLength); - return result; - } - - /** - * Calculate the position of the next comma or space or negative sign - * - * @param s the string to search - * @param start the position to start searching - * @param result the result of the extraction, including the position of the the starting position - * of next number, whether it is ending with a '-'. - */ - private static void extract(String s, int start, ExtractFloatResult result) { - // Now looking for ' ', ',', '.' or '-' from the start. - int currentIndex = start; - boolean foundSeparator = false; - result.mEndWithNegOrDot = false; - boolean secondDot = false; - boolean isExponential = false; - for (; currentIndex < s.length(); currentIndex++) { - boolean isPrevExponential = isExponential; - isExponential = false; - char currentChar = s.charAt(currentIndex); - switch (currentChar) { - case ' ': - case ',': - foundSeparator = true; - break; - case '-': - // The negative sign following a 'e' or 'E' is not a separator. - if (currentIndex != start && !isPrevExponential) { - foundSeparator = true; - result.mEndWithNegOrDot = true; - } - break; - case '.': - if (!secondDot) { - secondDot = true; - } else { - // This is the second dot, and it is considered as a separator. - foundSeparator = true; - result.mEndWithNegOrDot = true; - } - break; - case 'e': - case 'E': - isExponential = true; - break; - } - if (foundSeparator) { - break; - } - } - // When there is nothing found, then we put the end position to the end - // of the string. - result.mEndPosition = currentIndex; - } - - /** - * @param source The array of PathDataNode to be duplicated. - * @return a deep copy of the source. - */ - public static PathDataNode[] deepCopyNodes(PathDataNode[] source) { - if (source == null) { - return null; - } - PathDataNode[] copy = new PathParser.PathDataNode[source.length]; - for (int i = 0; i < source.length; i++) { - copy[i] = new PathDataNode(source[i]); - } - return copy; - } - - /** - * Update the target's data to match the source. Before calling this, make sure canMorph(target, - * source) is true. - * - * @param target The target path represented in an array of PathDataNode - * @param source The source path represented in an array of PathDataNode - * @hide - */ - public static void updateNodes(PathDataNode[] target, PathDataNode[] source) { - for (int i = 0; i < source.length; i++) { - target[i].mType = source[i].mType; - for (int j = 0; j < source[i].mParams.length; j++) { - target[i].mParams[j] = source[i].mParams[j]; - } - } - } - - /** - * Interpolate between two arrays of PathDataNodes with the given fraction, and store the results - * in the first parameter. - * - * @param target The resulting array of {@link PathDataNode} for the interpolation - * @param from The array of {@link PathDataNode} when fraction is 0 - * @param to The array of {@link PathDataNode} when the fraction is 1 - * @param fraction A float fraction value in the range of 0 to 1 - * @return whether it's possible to interpolate between the two arrays of PathDataNodes - * @see {@link #canMorph(PathDataNode[], PathDataNode[])} - */ - public static boolean interpolatePathDataNodes( - PathDataNode[] target, PathDataNode[] from, PathDataNode[] to, float fraction) { - if (target == null || from == null || to == null) { - throw new IllegalArgumentException( - "The nodes to be interpolated and resulting nodes" + " cannot be null"); - } - if (target.length != from.length || from.length != to.length) { - throw new IllegalArgumentException( - "The nodes to be interpolated and resulting nodes" + " must have the same length"); - } - if (!canMorph(from, to)) { - return false; - } - // Now do the interpolation - for (int i = 0; i < target.length; i++) { - target[i].interpolatePathDataNode(from[i], to[i], fraction); - } - return true; - } - - /** - * @param nodesFrom The source path represented in an array of PathDataNode - * @param nodesTo The target path represented in an array of PathDataNode - * @return whether the nodesFrom can morph into nodesTo - */ - public static boolean canMorph( - @Nullable PathDataNode[] nodesFrom, @Nullable PathDataNode[] nodesTo) { - if (nodesFrom == null || nodesTo == null) { - return false; - } - if (nodesFrom.length != nodesTo.length) { - return false; - } - for (int i = 0; i < nodesFrom.length; i++) { - if (nodesFrom[i].mType != nodesTo[i].mType - || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) { - return false; - } - } - return true; - } - - /** - * Each PathDataNode represents one command in the "d" attribute of the svg file. An array of - * PathDataNode can represent the whole "d" attribute. - */ - public static class PathDataNode { - /** @hide */ - public char mType; - /** @hide */ - public float[] mParams; - - PathDataNode(char type, float[] params) { - this.mType = type; - this.mParams = params; - } - - PathDataNode(PathDataNode n) { - mType = n.mType; - mParams = copyOfRange(n.mParams, 0, n.mParams.length); - } - /** - * Convert an array of PathDataNode to Path. - * - * @param node The source array of PathDataNode. - * @param path The target Path object. - */ - public static void nodesToPath(PathDataNode[] node, Path path) { - float[] current = new float[6]; - char previousCommand = 'm'; - for (int i = 0; i < node.length; i++) { - addCommand(path, current, previousCommand, node[i].mType, node[i].mParams); - previousCommand = node[i].mType; - } - } - - private static void addCommand( - Path path, float[] current, char previousCmd, char cmd, float[] val) { - int incr = 2; - float currentX = current[0]; - float currentY = current[1]; - float ctrlPointX = current[2]; - float ctrlPointY = current[3]; - float currentSegmentStartX = current[4]; - float currentSegmentStartY = current[5]; - float reflectiveCtrlPointX; - float reflectiveCtrlPointY; - switch (cmd) { - case 'z': - case 'Z': - path.close(); - // Path is closed here, but we need to move the pen to the - // closed position. So we cache the segment's starting position, - // and restore it here. - currentX = currentSegmentStartX; - currentY = currentSegmentStartY; - ctrlPointX = currentSegmentStartX; - ctrlPointY = currentSegmentStartY; - path.moveTo(currentX, currentY); - break; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - incr = 2; - break; - case 'h': - case 'H': - case 'v': - case 'V': - incr = 1; - break; - case 'c': - case 'C': - incr = 6; - break; - case 's': - case 'S': - case 'q': - case 'Q': - incr = 4; - break; - case 'a': - case 'A': - incr = 7; - break; - } - for (int k = 0; k < val.length; k += incr) { - switch (cmd) { - case 'm': // moveto - Start a new sub-path (relative) - currentX += val[k + 0]; - currentY += val[k + 1]; - if (k > 0) { - // According to the spec, if a moveto is followed by multiple - // pairs of coordinates, the subsequent pairs are treated as - // implicit lineto commands. - path.rLineTo(val[k + 0], val[k + 1]); - } else { - path.rMoveTo(val[k + 0], val[k + 1]); - currentSegmentStartX = currentX; - currentSegmentStartY = currentY; - } - break; - case 'M': // moveto - Start a new sub-path - currentX = val[k + 0]; - currentY = val[k + 1]; - if (k > 0) { - // According to the spec, if a moveto is followed by multiple - // pairs of coordinates, the subsequent pairs are treated as - // implicit lineto commands. - path.lineTo(val[k + 0], val[k + 1]); - } else { - path.moveTo(val[k + 0], val[k + 1]); - currentSegmentStartX = currentX; - currentSegmentStartY = currentY; - } - break; - case 'l': // lineto - Draw a line from the current point (relative) - path.rLineTo(val[k + 0], val[k + 1]); - currentX += val[k + 0]; - currentY += val[k + 1]; - break; - case 'L': // lineto - Draw a line from the current point - path.lineTo(val[k + 0], val[k + 1]); - currentX = val[k + 0]; - currentY = val[k + 1]; - break; - case 'h': // horizontal lineto - Draws a horizontal line (relative) - path.rLineTo(val[k + 0], 0); - currentX += val[k + 0]; - break; - case 'H': // horizontal lineto - Draws a horizontal line - path.lineTo(val[k + 0], currentY); - currentX = val[k + 0]; - break; - case 'v': // vertical lineto - Draws a vertical line from the current point (r) - path.rLineTo(0, val[k + 0]); - currentY += val[k + 0]; - break; - case 'V': // vertical lineto - Draws a vertical line from the current point - path.lineTo(currentX, val[k + 0]); - currentY = val[k + 0]; - break; - case 'c': // curveto - Draws a cubic Bézier curve (relative) - path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], val[k + 4], val[k + 5]); - ctrlPointX = currentX + val[k + 2]; - ctrlPointY = currentY + val[k + 3]; - currentX += val[k + 4]; - currentY += val[k + 5]; - break; - case 'C': // curveto - Draws a cubic Bézier curve - path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], val[k + 4], val[k + 5]); - currentX = val[k + 4]; - currentY = val[k + 5]; - ctrlPointX = val[k + 2]; - ctrlPointY = val[k + 3]; - break; - case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) - reflectiveCtrlPointX = 0; - reflectiveCtrlPointY = 0; - if (previousCmd == 'c' - || previousCmd == 's' - || previousCmd == 'C' - || previousCmd == 'S') { - reflectiveCtrlPointX = currentX - ctrlPointX; - reflectiveCtrlPointY = currentY - ctrlPointY; - } - path.rCubicTo( - reflectiveCtrlPointX, - reflectiveCtrlPointY, - val[k + 0], - val[k + 1], - val[k + 2], - val[k + 3]); - ctrlPointX = currentX + val[k + 0]; - ctrlPointY = currentY + val[k + 1]; - currentX += val[k + 2]; - currentY += val[k + 3]; - break; - case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) - reflectiveCtrlPointX = currentX; - reflectiveCtrlPointY = currentY; - if (previousCmd == 'c' - || previousCmd == 's' - || previousCmd == 'C' - || previousCmd == 'S') { - reflectiveCtrlPointX = 2 * currentX - ctrlPointX; - reflectiveCtrlPointY = 2 * currentY - ctrlPointY; - } - path.cubicTo( - reflectiveCtrlPointX, - reflectiveCtrlPointY, - val[k + 0], - val[k + 1], - val[k + 2], - val[k + 3]); - ctrlPointX = val[k + 0]; - ctrlPointY = val[k + 1]; - currentX = val[k + 2]; - currentY = val[k + 3]; - break; - case 'q': // Draws a quadratic Bézier (relative) - path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); - ctrlPointX = currentX + val[k + 0]; - ctrlPointY = currentY + val[k + 1]; - currentX += val[k + 2]; - currentY += val[k + 3]; - break; - case 'Q': // Draws a quadratic Bézier - path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); - ctrlPointX = val[k + 0]; - ctrlPointY = val[k + 1]; - currentX = val[k + 2]; - currentY = val[k + 3]; - break; - case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) - reflectiveCtrlPointX = 0; - reflectiveCtrlPointY = 0; - if (previousCmd == 'q' - || previousCmd == 't' - || previousCmd == 'Q' - || previousCmd == 'T') { - reflectiveCtrlPointX = currentX - ctrlPointX; - reflectiveCtrlPointY = currentY - ctrlPointY; - } - path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, val[k + 0], val[k + 1]); - ctrlPointX = currentX + reflectiveCtrlPointX; - ctrlPointY = currentY + reflectiveCtrlPointY; - currentX += val[k + 0]; - currentY += val[k + 1]; - break; - case 'T': // Draws a quadratic Bézier curve (reflective control point) - reflectiveCtrlPointX = currentX; - reflectiveCtrlPointY = currentY; - if (previousCmd == 'q' - || previousCmd == 't' - || previousCmd == 'Q' - || previousCmd == 'T') { - reflectiveCtrlPointX = 2 * currentX - ctrlPointX; - reflectiveCtrlPointY = 2 * currentY - ctrlPointY; - } - path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, val[k + 0], val[k + 1]); - ctrlPointX = reflectiveCtrlPointX; - ctrlPointY = reflectiveCtrlPointY; - currentX = val[k + 0]; - currentY = val[k + 1]; - break; - case 'a': // Draws an elliptical arc - // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) - drawArc( - path, - currentX, - currentY, - val[k + 5] + currentX, - val[k + 6] + currentY, - val[k + 0], - val[k + 1], - val[k + 2], - val[k + 3] != 0, - val[k + 4] != 0); - currentX += val[k + 5]; - currentY += val[k + 6]; - ctrlPointX = currentX; - ctrlPointY = currentY; - break; - case 'A': // Draws an elliptical arc - drawArc( - path, - currentX, - currentY, - val[k + 5], - val[k + 6], - val[k + 0], - val[k + 1], - val[k + 2], - val[k + 3] != 0, - val[k + 4] != 0); - currentX = val[k + 5]; - currentY = val[k + 6]; - ctrlPointX = currentX; - ctrlPointY = currentY; - break; - } - previousCmd = cmd; - } - current[0] = currentX; - current[1] = currentY; - current[2] = ctrlPointX; - current[3] = ctrlPointY; - current[4] = currentSegmentStartX; - current[5] = currentSegmentStartY; - } - - private static void drawArc( - Path p, - float x0, - float y0, - float x1, - float y1, - float a, - float b, - float theta, - boolean isMoreThanHalf, - boolean isPositiveArc) { - /* Convert rotation angle from degrees to radians */ - double thetaD = Math.toRadians(theta); - /* Pre-compute rotation matrix entries */ - double cosTheta = Math.cos(thetaD); - double sinTheta = Math.sin(thetaD); - /* Transform (x0, y0) and (x1, y1) into unit space */ - /* using (inverse) rotation, followed by (inverse) scale */ - double x0p = (x0 * cosTheta + y0 * sinTheta) / a; - double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; - double x1p = (x1 * cosTheta + y1 * sinTheta) / a; - double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; - /* Compute differences and averages */ - double dx = x0p - x1p; - double dy = y0p - y1p; - double xm = (x0p + x1p) / 2; - double ym = (y0p + y1p) / 2; - /* Solve for intersecting unit circles */ - double dsq = dx * dx + dy * dy; - if (dsq == 0.0) { - LOG.warn("Points are coincident"); - return; /* Points are coincident */ - } - double disc = 1.0 / dsq - 1.0 / 4.0; - if (disc < 0.0) { - LOG.warn("Points are too far apart " + dsq); - float adjust = (float) (Math.sqrt(dsq) / 1.99999); - drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta, isMoreThanHalf, isPositiveArc); - return; /* Points are too far apart */ - } - double s = Math.sqrt(disc); - double sdx = s * dx; - double sdy = s * dy; - double cx; - double cy; - if (isMoreThanHalf == isPositiveArc) { - cx = xm - sdy; - cy = ym + sdx; - } else { - cx = xm + sdy; - cy = ym - sdx; - } - double eta0 = Math.atan2((y0p - cy), (x0p - cx)); - double eta1 = Math.atan2((y1p - cy), (x1p - cx)); - double sweep = (eta1 - eta0); - if (isPositiveArc != (sweep >= 0)) { - if (sweep > 0) { - sweep -= 2 * Math.PI; - } else { - sweep += 2 * Math.PI; - } - } - cx *= a; - cy *= b; - double tcx = cx; - cx = cx * cosTheta - cy * sinTheta; - cy = tcx * sinTheta + cy * cosTheta; - arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); - } - - /** - * Converts an arc to cubic Bezier segments and records them in p. - * - * @param p The target for the cubic Bezier segments - * @param cx The x coordinate center of the ellipse - * @param cy The y coordinate center of the ellipse - * @param a The radius of the ellipse in the horizontal direction - * @param b The radius of the ellipse in the vertical direction - * @param e1x E(eta1) x coordinate of the starting point of the arc - * @param e1y E(eta2) y coordinate of the starting point of the arc - * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane - * @param start The start angle of the arc on the ellipse - * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse - */ - private static void arcToBezier( - Path p, - double cx, - double cy, - double a, - double b, - double e1x, - double e1y, - double theta, - double start, - double sweep) { - // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html - // and http://www.spaceroots.org/documents/ellipse/node22.html - // Maximum of 45 degrees per cubic Bezier segment - int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI)); - double eta1 = start; - double cosTheta = Math.cos(theta); - double sinTheta = Math.sin(theta); - double cosEta1 = Math.cos(eta1); - double sinEta1 = Math.sin(eta1); - double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); - double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); - double anglePerSegment = sweep / numSegments; - for (int i = 0; i < numSegments; i++) { - double eta2 = eta1 + anglePerSegment; - double sinEta2 = Math.sin(eta2); - double cosEta2 = Math.cos(eta2); - double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); - double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); - double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; - double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; - double tanDiff2 = Math.tan((eta2 - eta1) / 2); - double alpha = Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; - double q1x = e1x + alpha * ep1x; - double q1y = e1y + alpha * ep1y; - double q2x = e2x - alpha * ep2x; - double q2y = e2y - alpha * ep2y; - // Adding this no-op call to workaround a proguard related issue. - p.rLineTo(0, 0); - p.cubicTo((float) q1x, (float) q1y, (float) q2x, (float) q2y, (float) e2x, (float) e2y); - eta1 = eta2; - e1x = e2x; - e1y = e2y; - ep1x = ep2x; - ep1y = ep2y; - } - } - - /** - * The current PathDataNode will be interpolated between the nodeFrom and - * nodeTo according to the fraction. - * - * @param nodeFrom The start value as a PathDataNode. - * @param nodeTo The end value as a PathDataNode - * @param fraction The fraction to interpolate. - */ - public void interpolatePathDataNode( - PathDataNode nodeFrom, PathDataNode nodeTo, float fraction) { - mType = nodeFrom.mType; - for (int i = 0; i < nodeFrom.mParams.length; i++) { - mParams[i] = nodeFrom.mParams[i] * (1 - fraction) + nodeTo.mParams[i] * fraction; - } - } - } - - private static class ExtractFloatResult { - // We need to return the position of the next separator and whether the - // next float starts with a '-' or a '.'. - int mEndPosition; - boolean mEndWithNegOrDot; - - ExtractFloatResult() {} - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/ParserHelper.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/ParserHelper.java deleted file mode 100644 index 46c791d58..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/ParserHelper.java +++ /dev/null @@ -1,394 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.utilities.parser; - -/* - - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You 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. - - Changes Copyright 2011 Google Inc. -*/ - -/** - * Parses numbers from SVG text. Based on the Batik Number Parser (Apache 2 License). - * - * @author Apache Software Foundation, Larva Labs LLC - */ -class ParserHelper { - - /** Array of powers of ten. Using double instead of float gives a tiny bit more precision. */ - private static final double[] pow10 = new double[128]; - - static { - for (int i = 0; i < pow10.length; i++) { - pow10[i] = Math.pow(10, i); - } - } - - private final CharSequence s; - private final int n; - public int pos; - private char current; - - public ParserHelper(CharSequence s) { - this.s = s; - this.pos = 0; - n = s.length(); - current = s.charAt(pos); - } - - // Computes a float from mantissa and exponent. - private static float buildFloat(int mant, int exp) { - if (exp < -125 || mant == 0) { - return 0.0f; - } - - if (exp >= 128) { - return (mant > 0) ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY; - } - - if (exp == 0) { - return mant; - } - - if (mant >= (1 << 26)) { - mant++; // round up trailing bits if they will be dropped. - } - - return (float) ((exp > 0) ? mant * pow10[exp] : mant / pow10[-exp]); - } - - public void skipWhitespace() { - while (pos < n) { - if (Character.isWhitespace(s.charAt(pos))) { - advance(); - } else { - break; - } - } - } - - public void advance() { - current = read(); - } - - public float nextFloat() { - skipWhitespace(); - float f = parseFloat(); - skipNumberSeparator(); - return f; - } - - private char read() { - if (pos < n) { - pos++; - } - if (pos == n) { - return '\0'; - } else { - return s.charAt(pos); - } - } - - private void reportUnexpectedCharacterError(char c) { - throw new RuntimeException("Unexpected char '" + c + "'."); - } - - void skipNumberSeparator() { - while (pos < n) { - char c = s.charAt(pos); - switch (c) { - case ' ': - case ',': - case '\n': - case '\t': - advance(); - break; - default: - return; - } - } - } - - // Parses the content of the buffer and converts it to a float. - float parseFloat() { - int mant = 0; - int mantDig = 0; - boolean mantPos = true; - boolean mantRead = false; - - int exp = 0; - int expDig = 0; - int expAdj = 0; - boolean expPos = true; - - switch (current) { - case '-': - mantPos = false; - // fallthrough - case '+': - current = read(); - } - m1: - switch (current) { - default: - return Float.NaN; - - case '.': - break; - - case '0': - mantRead = true; - l: - for (; ; ) { - current = read(); - switch (current) { - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break l; - case '.': - case 'e': - case 'E': - break m1; - default: - return 0.0f; - case '0': - } - } - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - mantRead = true; - l: - for (; ; ) { - if (mantDig < 9) { - mantDig++; - mant = mant * 10 + (current - '0'); - } else { - expAdj++; - } - current = read(); - switch (current) { - default: - break l; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - } - } - } - - if (current == '.') { - current = read(); - m2: - switch (current) { - default: - case 'e': - case 'E': - if (!mantRead) { - reportUnexpectedCharacterError(current); - return 0.0f; - } - break; - - case '0': - if (mantDig == 0) { - l: - for (; ; ) { - current = read(); - expAdj--; - switch (current) { - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break l; - default: - if (!mantRead) { - return 0.0f; - } - break m2; - case '0': - } - } - } - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - l: - for (; ; ) { - if (mantDig < 9) { - mantDig++; - mant = mant * 10 + (current - '0'); - expAdj--; - } - current = read(); - switch (current) { - default: - break l; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - } - } - } - } - - switch (current) { - case 'e': - case 'E': - current = read(); - switch (current) { - default: - reportUnexpectedCharacterError(current); - return 0f; - case '-': - expPos = false; - case '+': - current = read(); - switch (current) { - default: - reportUnexpectedCharacterError(current); - return 0f; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - } - - en: - switch (current) { - case '0': - l: - for (; ; ) { - current = read(); - switch (current) { - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break l; - default: - break en; - case '0': - } - } - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - l: - for (; ; ) { - if (expDig < 3) { - expDig++; - exp = exp * 10 + (current - '0'); - } - current = read(); - switch (current) { - default: - break l; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - } - } - } - default: - } - - if (!expPos) { - exp = -exp; - } - exp += expAdj; - if (!mantPos) { - mant = -mant; - } - - return buildFloat(mant, exp); - } -} diff --git a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/PathParser.java b/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/PathParser.java deleted file mode 100644 index 7ba0d147e..000000000 --- a/xml-inflater/src/main/java/com/sdsmdg/harjot/vectormaster/utilities/parser/PathParser.java +++ /dev/null @@ -1,342 +0,0 @@ -package com.sdsmdg.harjot.vectormaster.utilities.parser; - -import android.graphics.Path; -import android.graphics.RectF; -import android.util.Log; - -public class PathParser { - - private static final String TAG = "PATH_PARSER"; - - /* - * This is where the hard-to-parse paths are handled. - * Uppercase rules are absolute positions, lowercase are relative. - * Types of path rules: - *

- *

    - *
  1. M/m - (x y)+ - Move to (without drawing) - *
  2. Z/z - (no params) - Close path (back to starting point) - *
  3. L/l - (x y)+ - Line to - *
  4. H/h - x+ - Horizontal ine to - *
  5. V/v - y+ - Vertical line to - *
  6. C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to - *
  7. S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from previous C/S is the x1, y1 of this bezier) - *
  8. Q/q - (x1 y1 x y)+ - Quadratic bezier to - *
  9. T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" of last one w.r.t. to current point) - *
- *

- * Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, (ie. begin with a - sign) - */ - public static Path doPath(String s) { - int n = s.length(); - ParserHelper ph = new ParserHelper(s); - ph.skipWhitespace(); - Path p = new Path(); - float lastX = 0; - float lastY = 0; - float lastX1 = 0; - float lastY1 = 0; - float contourInitialX = 0; - float contourInitialY = 0; - RectF r = new RectF(); - char prevCmd = 'm'; - char cmd = 'x'; - while (ph.pos < n) { - char next = s.charAt(ph.pos); - if (!Character.isDigit(next) && !(next == '.') && !(next == '-')) { - cmd = next; - ph.advance(); - } else if (cmd == 'M') { // implied command - cmd = 'L'; - } else if (cmd == 'm') { // implied command - cmd = 'l'; - } else { // implied command - // ignore - } - p.computeBounds(r, true); - // Log.d(TAG, " " + cmd + " " + r); - // Util.debug("* Commands remaining: '" + path + "'."); - boolean wasCurve = false; - switch (cmd) { - case 'M': - case 'm': - { - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 'm') { - p.rMoveTo(x, y); - lastX += x; - lastY += y; - } else { - p.moveTo(x, y); - lastX = x; - lastY = y; - } - contourInitialX = lastX; - contourInitialY = lastY; - break; - } - case 'Z': - case 'z': - { - /// p.lineTo(contourInitialX, contourInitialY); - p.close(); - lastX = contourInitialX; - lastY = contourInitialY; - break; - } - case 'L': - case 'l': - { - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 'l') { - if ((prevCmd == 'M' || prevCmd == 'm') && x == 0 && y == 0) { - p.addCircle(x, y, 1f, Path.Direction.CW); - } else { - p.rLineTo(x, y); - lastX += x; - lastY += y; - } - } else { - if ((prevCmd == 'M' || prevCmd == 'm') && x == lastX && y == lastY) { - p.addCircle(x, y, 1f, Path.Direction.CW); - } else { - p.lineTo(x, y); - lastX = x; - lastY = y; - } - } - break; - } - case 'H': - case 'h': - { - float x = ph.nextFloat(); - if (cmd == 'h') { - p.rLineTo(x, 0); - lastX += x; - } else { - p.lineTo(x, lastY); - lastX = x; - } - break; - } - case 'V': - case 'v': - { - float y = ph.nextFloat(); - if (cmd == 'v') { - p.rLineTo(0, y); - lastY += y; - } else { - p.lineTo(lastX, y); - lastY = y; - } - break; - } - case 'C': - case 'c': - { - wasCurve = true; - float x1 = ph.nextFloat(); - float y1 = ph.nextFloat(); - float x2 = ph.nextFloat(); - float y2 = ph.nextFloat(); - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 'c') { - x1 += lastX; - x2 += lastX; - x += lastX; - y1 += lastY; - y2 += lastY; - y += lastY; - } - p.cubicTo(x1, y1, x2, y2, x, y); - lastX1 = x2; - lastY1 = y2; - lastX = x; - lastY = y; - break; - } - case 'S': - case 's': - { - wasCurve = true; - float x2 = ph.nextFloat(); - float y2 = ph.nextFloat(); - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 's') { - x2 += lastX; - x += lastX; - y2 += lastY; - y += lastY; - } - float x1 = 2 * lastX - lastX1; - float y1 = 2 * lastY - lastY1; - p.cubicTo(x1, y1, x2, y2, x, y); - lastX1 = x2; - lastY1 = y2; - lastX = x; - lastY = y; - break; - } - case 'A': - case 'a': - { - float rx = ph.nextFloat(); - float ry = ph.nextFloat(); - float theta = ph.nextFloat(); - int largeArc = (int) ph.nextFloat(); - int sweepArc = (int) ph.nextFloat(); - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 'a') { - x += lastX; - y += lastY; - } - drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc == 1, sweepArc == 1); - lastX = x; - lastY = y; - break; - } - case 'T': - case 't': - { - wasCurve = true; - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 't') { - x += lastX; - y += lastY; - } - float x1 = 2 * lastX - lastX1; - float y1 = 2 * lastY - lastY1; - p.cubicTo(lastX, lastY, x1, y1, x, y); - lastX = x; - lastY = y; - lastX1 = x1; - lastY1 = y1; - break; - } - case 'Q': - case 'q': - { - wasCurve = true; - float x1 = ph.nextFloat(); - float y1 = ph.nextFloat(); - float x = ph.nextFloat(); - float y = ph.nextFloat(); - if (cmd == 'q') { - x += lastX; - y += lastY; - x1 += lastX; - y1 += lastY; - } - p.cubicTo(lastX, lastY, x1, y1, x, y); - lastX1 = x1; - lastY1 = y1; - lastX = x; - lastY = y; - break; - } - default: - Log.w(TAG, "Invalid path command: " + cmd); - ph.advance(); - } - prevCmd = cmd; - if (!wasCurve) { - lastX1 = lastX; - lastY1 = lastY; - } - ph.skipWhitespace(); - } - return p; - } - - /* - * Elliptical arc implementation based on the SVG specification notes - * Adapted from the Batik library (Apache-2 license) by SAU - */ - private static void drawArc( - Path path, - double x0, - double y0, - double x, - double y, - double rx, - double ry, - double angle, - boolean largeArcFlag, - boolean sweepFlag) { - double dx2 = (x0 - x) / 2.0; - double dy2 = (y0 - y) / 2.0; - angle = Math.toRadians(angle % 360.0); - double cosAngle = Math.cos(angle); - double sinAngle = Math.sin(angle); - - double x1 = (cosAngle * dx2 + sinAngle * dy2); - double y1 = (-sinAngle * dx2 + cosAngle * dy2); - rx = Math.abs(rx); - ry = Math.abs(ry); - - double Prx = rx * rx; - double Pry = ry * ry; - double Px1 = x1 * x1; - double Py1 = y1 * y1; - - // check that radii are large enough - double radiiCheck = Px1 / Prx + Py1 / Pry; - if (radiiCheck > 1) { - rx = Math.sqrt(radiiCheck) * rx; - ry = Math.sqrt(radiiCheck) * ry; - Prx = rx * rx; - Pry = ry * ry; - } - - // Step 2 : Compute (cx1, cy1) - double sign = (largeArcFlag == sweepFlag) ? -1 : 1; - double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)); - sq = (sq < 0) ? 0 : sq; - double coef = (sign * Math.sqrt(sq)); - double cx1 = coef * ((rx * y1) / ry); - double cy1 = coef * -((ry * x1) / rx); - - double sx2 = (x0 + x) / 2.0; - double sy2 = (y0 + y) / 2.0; - double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1); - double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1); - - // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) - double ux = (x1 - cx1) / rx; - double uy = (y1 - cy1) / ry; - double vx = (-x1 - cx1) / rx; - double vy = (-y1 - cy1) / ry; - double p, n; - - // Compute the angle start - n = Math.sqrt((ux * ux) + (uy * uy)); - p = ux; // (1 * ux) + (0 * uy) - sign = (uy < 0) ? -1.0 : 1.0; - double angleStart = Math.toDegrees(sign * Math.acos(p / n)); - - // Compute the angle extent - n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)); - p = ux * vx + uy * vy; - sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0; - double angleExtent = Math.toDegrees(sign * Math.acos(p / n)); - if (!sweepFlag && angleExtent > 0) { - angleExtent -= 360f; - } else if (sweepFlag && angleExtent < 0) { - angleExtent += 360f; - } - angleExtent %= 360f; - angleStart %= 360f; - - RectF oval = - new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry)); - path.addArc(oval, (float) angleStart, (float) angleExtent); - } -}