├── .gitignore ├── LICENSE ├── README.md ├── TODO.txt ├── app ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── spiritcroc │ │ └── modular_remote │ │ ├── BReceiver.java │ │ ├── BaseActivity.java │ │ ├── CustomHorizontalScrollView.java │ │ ├── CustomPreferenceFragment.java │ │ ├── CustomScrollView.java │ │ ├── CustomViewPager.java │ │ ├── Display.java │ │ ├── DragManager.java │ │ ├── GlobalActionHandler.java │ │ ├── GlobalActionSetting.java │ │ ├── GlobalKeyService.java │ │ ├── MainActivity.java │ │ ├── Preferences.java │ │ ├── ReceiverIpSelectorUser.java │ │ ├── ResizeFrame.java │ │ ├── SettingsActivity.java │ │ ├── SettingsAdvancedFragment.java │ │ ├── SettingsFragment.java │ │ ├── SettingsGlobalActionsFragment.java │ │ ├── SettingsMoreSpaceFragment.java │ │ ├── ShortcutActivity.java │ │ ├── TcpConnectionManager.java │ │ ├── TcpInformation.java │ │ ├── TimeSingleton.java │ │ ├── TouchOverlay.java │ │ ├── Util.java │ │ ├── dialogs │ │ ├── AboutDialog.java │ │ ├── AddCommandLineFragmentDialog.java │ │ ├── AddDisplayFragmentDialog.java │ │ ├── AddFragmentDialog.java │ │ ├── AddPageDialog.java │ │ ├── AddSpinnerFragmentDialog.java │ │ ├── AddToggleFragmentDialog.java │ │ ├── AddWebViewFragmentDialog.java │ │ ├── CommandInterface.java │ │ ├── ConfigureToggleDialog.java │ │ ├── CustomDialogFragment.java │ │ ├── EditConnectionDialog.java │ │ ├── EnterRawCommandDialog.java │ │ ├── GlobalActionDialog.java │ │ ├── GreetingDialog.java │ │ ├── HideSubmenuItemsDialog.java │ │ ├── OverlapWarningDialog.java │ │ ├── OverwriteTypeDialog.java │ │ ├── RemoveConnectionDialog.java │ │ ├── RemoveContainerDialog.java │ │ ├── SelectCommandDialog.java │ │ ├── SelectConnectionDialog.java │ │ ├── SelectContainerDialog.java │ │ ├── SelectEditSubmenuDialog.java │ │ ├── SelectFragmentsDialog.java │ │ ├── SetupGridSizeDialog.java │ │ ├── SortFragmentsDialog.java │ │ └── SortPagesDialog.java │ │ ├── dslv │ │ ├── DragSortController.java │ │ ├── DragSortCursorAdapter.java │ │ ├── DragSortItemView.java │ │ ├── DragSortItemViewCheckable.java │ │ ├── DragSortListView.java │ │ ├── ResourceDragSortCursorAdapter.java │ │ ├── SimpleDragSortCursorAdapter.java │ │ └── SimpleFloatViewManager.java │ │ └── modules │ │ ├── CommandLineFragment.java │ │ ├── Container.java │ │ ├── DisplayFragment.java │ │ ├── HorizontalContainerFragment.java │ │ ├── ModuleFragment.java │ │ ├── PageContainerFragment.java │ │ ├── ScrollContainerFragment.java │ │ ├── SpinnerFragment.java │ │ ├── ToggleFragment.java │ │ ├── WebViewFragment.java │ │ └── WidgetContainerFragment.java │ └── res │ ├── drawable-hdpi │ ├── ic_action_container_drag_24dp.png │ ├── ic_action_container_scroll_24dp.png │ ├── ic_add_white_24dp.png │ ├── ic_create_white_24dp.png │ ├── ic_tune_white_24dp.png │ ├── ic_widget_resize_handle.png │ ├── widget_resize_frame.9.png │ └── widget_resize_shadow.9.png │ ├── drawable-mdpi │ ├── ic_action_container_drag_24dp.png │ ├── ic_action_container_scroll_24dp.png │ ├── ic_add_white_24dp.png │ ├── ic_create_white_24dp.png │ ├── ic_tune_white_24dp.png │ ├── ic_widget_resize_handle.png │ ├── widget_resize_frame.9.png │ └── widget_resize_shadow.9.png │ ├── drawable-xhdpi │ ├── ic_action_container_drag_24dp.png │ ├── ic_action_container_scroll_24dp.png │ ├── ic_add_white_24dp.png │ ├── ic_create_white_24dp.png │ ├── ic_tune_white_24dp.png │ ├── ic_widget_resize_handle.png │ ├── widget_resize_frame.9.png │ └── widget_resize_shadow.9.png │ ├── drawable-xxhdpi │ ├── ic_action_container_drag_24dp.png │ ├── ic_action_container_scroll_24dp.png │ ├── ic_add_white_24dp.png │ ├── ic_create_white_24dp.png │ ├── ic_tune_white_24dp.png │ ├── ic_widget_resize_handle.png │ ├── widget_resize_frame.9.png │ └── widget_resize_shadow.9.png │ ├── drawable-xxxhdpi │ ├── ic_action_container_drag_24dp.png │ ├── ic_action_container_scroll_24dp.png │ ├── ic_add_white_24dp.png │ ├── ic_create_white_24dp.png │ ├── ic_tune_white_24dp.png │ ├── ic_widget_resize_handle.png │ ├── widget_resize_frame.9.png │ └── widget_resize_shadow.9.png │ ├── drawable │ └── ic_launcher_monochrome.xml │ ├── layout │ ├── abcd_alert_dialog_button_bar_material.xml │ ├── abcd_alert_dialog_material.xml │ ├── abcd_alert_dialog_title_material.xml │ ├── activity_main.xml │ ├── activity_shortcut.xml │ ├── dialog_about.xml │ ├── dialog_add_page.xml │ ├── dialog_command_line_fragment.xml │ ├── dialog_configure_toggle.xml │ ├── dialog_display_fragment.xml │ ├── dialog_edit_connection.xml │ ├── dialog_enter_raw_command.xml │ ├── dialog_global_action.xml │ ├── dialog_select_fragment.xml │ ├── dialog_setup_grid_size.xml │ ├── dialog_sort.xml │ ├── dialog_spinner_fragment.xml │ ├── dialog_toggle_fragment.xml │ ├── dialog_web_view_fragment.xml │ ├── fragment_button.xml │ ├── fragment_command_line.xml │ ├── fragment_display.xml │ ├── fragment_horizontal_container.xml │ ├── fragment_page_container.xml │ ├── fragment_scroll_container.xml │ ├── fragment_spinner.xml │ ├── fragment_toggle.xml │ ├── fragment_web_view.xml │ └── fragment_widget_container.xml │ ├── menu │ ├── menu_fragment_edit.xml │ ├── menu_main.xml │ └── menu_main_edit.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_foreground.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_foreground.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_foreground.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_foreground.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_foreground.png │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── dslv_attrs.xml │ ├── strings.xml │ ├── strings_activity_settings.xml │ ├── strings_tcp_commands_general.xml │ ├── strings_tcp_commands_pioneer.xml │ └── styles.xml │ └── xml │ ├── preferences.xml │ ├── preferences_advanced.xml │ ├── preferences_global_actions.xml │ └── preferences_more_space.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── guide_supporting_new_devices.md ├── icons ├── ic_action_container_drag_24dp.svg ├── ic_action_container_drag_36dp.svg ├── ic_action_container_scroll_24dp.svg ├── ic_action_container_scroll_36dp.svg └── ic_launcher.svg ├── known_reference_sheets.md ├── settings.gradle └── store ├── .featured-image.svg ├── description_full.txt ├── descritption_short.txt ├── featured-image.png ├── ic_launcher.png └── screenshots ├── Screenshot_01.png ├── Screenshot_02.png └── Screenshot_03.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.apk 2 | *.iml 3 | output.json 4 | .gradle 5 | /local.properties 6 | /.idea 7 | /projectFilesBackup 8 | .DS_Store 9 | /build 10 | /captures 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modular Remote 2 | 3 | The aim of this app is to provide the user a possibility to fully create their own remote out of several modules. 4 | Note it's no longer in active development, but I still use it to arrange a couple of webviews next to each other in pages, so I may push some updates every few years or so. 5 | 6 | 7 | 8 | 9 | Modular Remote is free, and open source. 10 | If you want to help by adding support for new remotely control-able devices, read [this guide](https://github.com/SpiritCroc/Modular-Remote/blob/master/guide_supporting_new_devices.md). 11 | Some very old to-do list can be found [here](https://github.com/SpiritCroc/Modular-Remote/blob/master/TODO.txt). 12 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | Stuff that could or should be done. 2 | The more x, the more important 3 | 4 | WIP: 5 | x - pioneer: complete strings_tcp_commands 6 | 7 | Fixes/Improvements: 8 | - if trying to add fragment to page that is null (can happen with wrong recreationKey), make error message (instead of crash?) 9 | 10 | Upgrades / New stuff: 11 | 12 | no coding required: 13 | - better featured image for play store (http://android-developers.blogspot.de/2011/10/android-market-featured-image.html) 14 | - more screenshots 15 | - feature video 16 | 17 | module upgrades: 18 | - button: type general: home, optionsMenu, open device volume dialog, display off, fullscreen on/off, navbar on/off, pagetabs on/off (last three also as response for toggle?) 19 | - button: possibility: image instead of written tag (built in, external source?) 20 | x - style: style editor → define global styles, each module references one style to apply 21 | select default style (used for new modules or when specified style not found) 22 | always at least one style (→ reset button?) 23 | style attributes: 24 | text color 25 | text size 26 | bg color 27 | … 28 | (also) apply for multiple modules in SelectModuleFragment? 29 | - Spinner: possibility to change order 30 | x - Page: Use toast as display: multi-selection-list to select displays (→toast can be used for several displays) 31 | Example usage: use as error display 32 | each display: possibility to chose between Toast.LENGTH_SHORT and Toast.LENGTH_LONG 33 | 34 | UI upgrades: 35 | - dialogFragments: add to back stack (particularly when selecting commands / adding fragments) 36 | @Override 37 | public void show(@NonNull FragmentManager manager, String tag) { 38 | super.show(manager, tag); 39 | getFragmentManager().beginTransaction().addToBackStack(null).commit();//Issue: dialogs are never shown again despite being in back stack 40 | } 41 | - immersive mode: 42 | when showing systemUI elements in immersive mode, hide elements again on click somewhere else 43 | when showing systemUI elements in immersive mode, do not allow clicks on other UI elements (prevent sending commands by accident) 44 | do not allow systemUI timeout < 2 or something 45 | if hidden menuBar appears, make it transparent (also other stuff transparent?) 46 | remove experimental: test on other devices 47 | fix: if actionBar hidden: issues when swiping down from statusBar (swiping up from navBar works) 48 | fix: possible not hiding actionBar after adding page 49 | fix: viewPager sometimes not updating on systemUI visibility changed 50 | - global padding settings, activity margin setting 51 | - CustomHorizontalScrollView + CustomScrollView: make snap depending on children measured size (inspiration by ChameleonOS app sidebar) 52 | 53 | other upgrades: 54 | - AddDisplayDialog: more characters in button_label_spinner_chars 55 | test on different APIs → don't show chars that are known to not show on certain APIs 56 | - first start: 57 | explanation how remote works 58 | 59 | New modules: 60 | - ExpandableContainer: 61 | option: onCreate: 62 | always expanded 63 | always collapsed 64 | remember from last session 65 | collapse if content has no connected connection + expand if reconnected 66 | - multi-function-button (as toggleButton, but with more (n) states (and no response)) 67 | - Slider: e.g. for volume 68 | 69 | Functional upgrades: 70 | - option to keep screen on 71 | x - CommandEditor: Create chained commands (→ submenu "custom"), per RECEIVER_TYPE basis 72 | - Pref: remember last page 73 | - Settings: import/export fragments (portrait/landscape) → 4 preferences (allow fragment migration across different orientations) 74 | 75 | 76 | Stuff someone could add if they feel bored 77 | - WebViewFragment: optional actionBar with back, forward, reload-buttons (→if active, no optionsMenuEntry) 78 | - keyboard functionality 79 | - own toolbar instead of AppCompatActivity 80 | - infrared FB? → ConsumerIrManager; https://stackoverflow.com/questions/28794899/ir-emitter-and-android-5-0 81 | - Add Help-button to Add…Dialog → ExplainDialog to explain options 82 | - use different icon for launcher shortcuts than for launcher (take ic_launcher and change letters M and R to P and S or S and C or something) 83 | - own typeface based on Roboto (get from AOSP) with special chars (play, pause, etc. → ensure visibility on all devices) 84 | - tasker/locale plugin to send tcp commands 85 | - custom widgets 86 | - custom notifications -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | def getGitDescription = { -> 4 | try { 5 | def out = new ByteArrayOutputStream() 6 | exec { 7 | commandLine 'git', 'describe', '--tags', '--dirty' 8 | standardOutput = out 9 | } 10 | return out.toString().trim() 11 | } 12 | catch (err) { 13 | return null 14 | } 15 | } 16 | 17 | android { 18 | compileSdkVersion 33 19 | buildToolsVersion '29.0.2' 20 | defaultConfig { 21 | applicationId 'de.spiritcroc.modular_remote' 22 | minSdkVersion 14 23 | targetSdkVersion 33 24 | versionCode 9 25 | versionName '0.7' 26 | buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L" 27 | buildConfigField "String", "GIT_DESCRIBE", "\"" + getGitDescription() + "\"" 28 | } 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | productFlavors { 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(dir: 'libs', include: ['*.jar']) 41 | implementation 'androidx.appcompat:appcompat:1.1.0' 42 | implementation 'androidx.preference:preference:1.1.0' 43 | implementation 'androidx.viewpager:viewpager:1.0.0' 44 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 45 | implementation 'androidx.legacy:legacy-support-v13:1.0.0' 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/BReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.net.ConnectivityManager; 25 | 26 | public class BReceiver extends BroadcastReceiver { 27 | @Override 28 | public void onReceive(Context context, Intent intent) { 29 | if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { 30 | TcpConnectionManager.getInstance(context.getApplicationContext()) 31 | .refreshConnections(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.ComponentName; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.ServiceConnection; 25 | import android.os.Bundle; 26 | import android.os.Handler; 27 | import android.os.IBinder; 28 | import android.os.Message; 29 | import android.os.Messenger; 30 | import android.os.RemoteException; 31 | import android.util.Log; 32 | 33 | import androidx.appcompat.app.AppCompatActivity; 34 | 35 | public abstract class BaseActivity extends AppCompatActivity { 36 | 37 | private static final String LOG_TAG = BaseActivity.class.getSimpleName(); 38 | 39 | private static final boolean DEBUG = false; 40 | 41 | private Messenger mGlobalKeyService = null; 42 | 43 | private Messenger mGlobalKeyListener = new Messenger(new Handler() { 44 | @Override 45 | public void handleMessage(Message msg) { 46 | onGlobalKeyMessage(msg); 47 | } 48 | }); 49 | 50 | private boolean mGlobalKeyServiceBound = false; 51 | 52 | protected ServiceConnection mGlobalKeyServiceConnection = new ServiceConnection() { 53 | @Override 54 | public void onServiceConnected(ComponentName componentName, IBinder iBinder) { 55 | mGlobalKeyService = new Messenger(iBinder); 56 | mGlobalKeyServiceBound = true; 57 | } 58 | 59 | @Override 60 | public void onServiceDisconnected(ComponentName componentName) { 61 | mGlobalKeyService = null; 62 | mGlobalKeyServiceBound = false; 63 | } 64 | }; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | startService(new Intent(this, GlobalKeyService.class)); 70 | } 71 | 72 | @Override 73 | protected void onStart() { 74 | super.onStart(); 75 | bindService(new Intent(this, GlobalKeyService.class), mGlobalKeyServiceConnection, 76 | Context.BIND_AUTO_CREATE); 77 | } 78 | 79 | @Override 80 | protected void onStop() { 81 | super.onStop(); 82 | if (mGlobalKeyServiceBound) { 83 | unbindService(mGlobalKeyServiceConnection); 84 | mGlobalKeyServiceBound = false; 85 | } 86 | } 87 | 88 | @Override 89 | protected void onResume() { 90 | super.onResume(); 91 | Message msg = Message.obtain(null, GlobalKeyService.ACTION_REGISTER_CLIENT); 92 | msg.replyTo = mGlobalKeyListener; 93 | sendGlobalKeyServiceMsg(msg); 94 | } 95 | 96 | @Override 97 | protected void onPause() { 98 | super.onPause(); 99 | Message msg = Message.obtain(null, GlobalKeyService.ACTION_UNREGISTER_CLIENT); 100 | msg.replyTo = mGlobalKeyListener; 101 | sendGlobalKeyServiceMsg(msg); 102 | } 103 | 104 | public void sendGlobalKeyServiceMsg(Message message) { 105 | if (!mGlobalKeyServiceBound) { 106 | new RuntimeException("Discarding message, required service is not bound").printStackTrace(); 107 | return; 108 | } 109 | try { 110 | mGlobalKeyService.send(message); 111 | } catch (RemoteException e) { 112 | e.printStackTrace(); 113 | } 114 | } 115 | 116 | protected void onGlobalKeyMessage(Message message) { 117 | if (DEBUG) Log.v(LOG_TAG, "Received message " + message); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/CustomHorizontalScrollView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.Context; 22 | 23 | import androidx.annotation.NonNull; 24 | import androidx.core.view.MotionEventCompat; 25 | import android.util.AttributeSet; 26 | import android.view.MotionEvent; 27 | import android.view.View; 28 | import android.widget.HorizontalScrollView; 29 | 30 | import de.spiritcroc.modular_remote.modules.HorizontalContainerFragment; 31 | 32 | /** 33 | * Enables reasonable support for HorizontalContainerFragments within HorizontalContainerFragments 34 | */ 35 | 36 | public class CustomHorizontalScrollView extends HorizontalScrollView { 37 | private float pointerX; 38 | 39 | private HorizontalContainerFragment wrapFragment; 40 | 41 | public CustomHorizontalScrollView(Context context, AttributeSet attrs) { 42 | super (context, attrs); 43 | } 44 | 45 | @Override 46 | public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) { 47 | final int action = MotionEventCompat.getActionMasked(ev); 48 | 49 | boolean result; 50 | 51 | switch (action) { 52 | case MotionEvent.ACTION_MOVE: 53 | if (!canScroll()) { 54 | // If it can't scroll, don't intercept touch events 55 | result = false; 56 | } else if ((ev.getX() > pointerX && !canScrollLeft()) || 57 | (ev.getX() < pointerX && !canScrollRight())) { 58 | result = false; 59 | } else { 60 | if (wrapFragment != null && !wrapFragment.hasChildWithScrollX()) { 61 | // We're the smallest scroll view with this direction in this hierarchy, 62 | // so disallow the parents 63 | View view = this; 64 | while (view.getParent() != null && view.getParent() instanceof View) { 65 | if (view.getParent() instanceof CustomHorizontalScrollView) { 66 | // Only steal touch event from views with the same direction 67 | view.getParent().requestDisallowInterceptTouchEvent(true); 68 | } 69 | view = (View) view.getParent(); 70 | } 71 | } 72 | result = super.onInterceptTouchEvent(ev); 73 | } 74 | break; 75 | default: 76 | result = super.onInterceptTouchEvent(ev); 77 | } 78 | 79 | pointerX = ev.getX(); 80 | 81 | return result; 82 | } 83 | 84 | public void setWrapFragment(HorizontalContainerFragment wrapFragment) { 85 | this.wrapFragment = wrapFragment; 86 | } 87 | 88 | private boolean canScrollLeft() { 89 | return getScrollX() != 0; 90 | } 91 | private boolean canScrollRight() { 92 | return getMeasuredWidth() + getScrollX() != getChildAt(0).getMeasuredWidth(); 93 | } 94 | public boolean canScroll() { 95 | return getMeasuredWidth() < getChildAt(0).getMeasuredWidth(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/CustomPreferenceFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.app.Activity; 22 | import android.content.SharedPreferences; 23 | import android.os.Message; 24 | import android.util.Log; 25 | 26 | import androidx.preference.Preference; 27 | import androidx.preference.PreferenceFragmentCompat; 28 | 29 | public abstract class CustomPreferenceFragment extends PreferenceFragmentCompat { 30 | private static final String LOG_TAG = CustomPreferenceFragment.class.getSimpleName(); 31 | 32 | @Override 33 | public boolean onPreferenceTreeClick(Preference preference) { 34 | Activity activity = getActivity(); 35 | if (activity instanceof SettingsActivity) { 36 | return ((SettingsActivity) activity).onPreferenceClick(preference) || 37 | super.onPreferenceTreeClick(preference); 38 | } else { 39 | Log.w(LOG_TAG, "activity not instanceof SettingsActivity"); 40 | return super.onPreferenceTreeClick(preference); 41 | } 42 | } 43 | 44 | protected void onGlobalKeyMessage(Message message) {} 45 | 46 | // Util methods 47 | 48 | protected int correctInteger(SharedPreferences sharedPreferences, String key, String value, 49 | int defaultValue){ 50 | try { 51 | return Integer.parseInt(value); 52 | } catch (Exception e) { 53 | SharedPreferences.Editor editor = sharedPreferences.edit(); 54 | editor.putString(key, String.valueOf(defaultValue)); 55 | editor.apply(); 56 | return defaultValue; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/CustomScrollView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.Context; 22 | 23 | import androidx.annotation.NonNull; 24 | import androidx.core.view.MotionEventCompat; 25 | import android.util.AttributeSet; 26 | import android.view.MotionEvent; 27 | import android.view.View; 28 | import android.widget.ScrollView; 29 | 30 | import de.spiritcroc.modular_remote.modules.ScrollContainerFragment; 31 | 32 | /** 33 | * Enables reasonable support for ScrollContainerFragments within ScrollContainerFragments 34 | */ 35 | 36 | public class CustomScrollView extends ScrollView { 37 | private float pointerY; 38 | 39 | private ScrollContainerFragment wrapFragment; 40 | 41 | public CustomScrollView(Context context, AttributeSet attrs) { 42 | super (context, attrs); 43 | } 44 | 45 | @Override 46 | public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) { 47 | final int action = MotionEventCompat.getActionMasked(ev); 48 | 49 | boolean result; 50 | 51 | switch (action) { 52 | case MotionEvent.ACTION_MOVE: 53 | if (!canScroll()) { 54 | // If it can't scroll, don't intercept touch events 55 | result = false; 56 | } else if ((ev.getY() > pointerY && !canScrollUp()) || 57 | (ev.getY() < pointerY && !canScrollDown())) { 58 | result = false; 59 | } else { 60 | if (wrapFragment != null && !wrapFragment.hasChildWithScrollY()) { 61 | // We're the smallest scroll view with this direction in this hierarchy, 62 | // so disallow the parents 63 | View view = this; 64 | while (view.getParent() != null && view.getParent() instanceof View) { 65 | if (view.getParent() instanceof CustomScrollView) { 66 | // Only steal touch event from views with the same direction 67 | view.getParent().requestDisallowInterceptTouchEvent(true); 68 | } 69 | view = (View) view.getParent(); 70 | } 71 | } 72 | result = super.onInterceptTouchEvent(ev); 73 | } 74 | break; 75 | default: 76 | result = super.onInterceptTouchEvent(ev); 77 | } 78 | 79 | pointerY = ev.getY(); 80 | 81 | return result; 82 | } 83 | 84 | public void setWrapFragment(ScrollContainerFragment wrapFragment) { 85 | this.wrapFragment = wrapFragment; 86 | } 87 | 88 | private boolean canScrollUp() { 89 | return getScrollY() != 0; 90 | } 91 | private boolean canScrollDown() { 92 | return getMeasuredHeight() + getScrollY() != getChildAt(0).getMeasuredHeight(); 93 | } 94 | public boolean canScroll() { 95 | return getMeasuredHeight() < getChildAt(0).getMeasuredHeight(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/CustomViewPager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.Context; 22 | import android.util.AttributeSet; 23 | import android.view.MotionEvent; 24 | 25 | import androidx.viewpager.widget.ViewPager; 26 | 27 | public class CustomViewPager extends ViewPager { 28 | private boolean scrollEnabled = true; 29 | 30 | public CustomViewPager(Context context) { 31 | super(context); 32 | } 33 | 34 | public CustomViewPager(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | } 37 | 38 | @Override 39 | public boolean onTouchEvent(MotionEvent event) { 40 | return scrollEnabled && super.onTouchEvent(event); 41 | } 42 | 43 | @Override 44 | public boolean onInterceptTouchEvent(MotionEvent event) { 45 | return scrollEnabled && super.onInterceptTouchEvent(event); 46 | } 47 | 48 | public void setScrollEnabled(boolean scrollEnabled) { 49 | this.scrollEnabled = scrollEnabled; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/DragManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.util.Log; 22 | 23 | import de.spiritcroc.modular_remote.modules.Container; 24 | import de.spiritcroc.modular_remote.modules.ModuleFragment; 25 | 26 | public class DragManager { 27 | private static final String LOG_TAG = DragManager.class.getSimpleName(); 28 | private static final boolean DEBUG = false; 29 | 30 | private static ModuleFragment dragFragment; 31 | 32 | private DragManager(){} 33 | 34 | public static boolean startDrag(ModuleFragment fragment) { 35 | // Make sure that parent containers don't replace dragged fragment 36 | if (dragFragment == null || 37 | !(fragment instanceof Container)) { 38 | if (DEBUG) Log.v(LOG_TAG, "Start drag: " + fragment); 39 | dragFragment = fragment; 40 | fragment.onStartDrag(); 41 | return true; 42 | } else { 43 | if (DEBUG) Log.d(LOG_TAG, "Don't start drag for " + fragment + ": already dragging " + 44 | dragFragment); 45 | return false; 46 | } 47 | } 48 | public static ModuleFragment stopDrag() { 49 | ModuleFragment fragment = dragFragment; 50 | if (DEBUG) Log.v(LOG_TAG, "Stop drag: " + fragment); 51 | dragFragment = null; 52 | if (fragment != null) { 53 | fragment.onStopDrag(); 54 | } 55 | return fragment; 56 | } 57 | 58 | private static float dragStartX, dragStartY; 59 | public static void setLongPressPos(float dragStartX, float dragStartY) { 60 | DragManager.dragStartX = dragStartX; 61 | DragManager.dragStartY = dragStartY; 62 | } 63 | public static float getDragStartX() { 64 | return dragStartX; 65 | } 66 | public static float getDragStartY() { 67 | return dragStartY; 68 | } 69 | public static void cancelLongPress() { 70 | dragStartX = -1; 71 | } 72 | public static boolean isLongPressPossible() { 73 | return dragStartX != -1; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/GlobalActionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.annotation.TargetApi; 22 | import android.content.SharedPreferences; 23 | import android.os.Message; 24 | import android.preference.PreferenceManager; 25 | 26 | @TargetApi(21) 27 | public abstract class GlobalActionHandler { 28 | private static final String LOG_TAG = GlobalActionHandler.class.getSimpleName(); 29 | 30 | public static void enable(BaseActivity context) { 31 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); 32 | Message volUpMsg = Message.obtain(null, GlobalKeyService.ACTION_ADD_ACTION, GlobalKeyService.EXTRA_ACTION_ID_VOLUME_UP, 0, sp.getString(Preferences.GLOBAL_ACTION_VOLUME_UP, "")); 33 | Message volDownMsg = Message.obtain(null, GlobalKeyService.ACTION_ADD_ACTION, GlobalKeyService.EXTRA_ACTION_ID_VOLUME_DOWN, 0, sp.getString(Preferences.GLOBAL_ACTION_VOLUME_DOWN, "")); 34 | context.sendGlobalKeyServiceMsg(volUpMsg); 35 | context.sendGlobalKeyServiceMsg(volDownMsg); 36 | } 37 | 38 | public static void disable(BaseActivity context) { 39 | Message msg = Message.obtain(null, GlobalKeyService.ACTION_REMOVE_ALL); 40 | context.sendGlobalKeyServiceMsg(msg); 41 | } 42 | 43 | /* todo (best to move all of this class to BaseActivity?) 44 | public static void updateSetting(Context context, GlobalActionSetting setting) { 45 | if (appContext == null) { 46 | Log.e(LOG_TAG, "updateSettings: missing context"); 47 | return; 48 | } 49 | String key; 50 | if (setting == volumeUpSetting) { 51 | key = Preferences.GLOBAL_ACTION_VOLUME_UP; 52 | } else if (setting == volumeDownSetting) { 53 | key = Preferences.GLOBAL_ACTION_VOLUME_DOWN; 54 | } else { 55 | Log.w(LOG_TAG, "updateSetting: requested by unknown action " + setting); 56 | return; 57 | } 58 | PreferenceManager.getDefaultSharedPreferences(appContext).edit() 59 | .putString(key, setting.getRecreationKey()).apply(); 60 | } 61 | */ 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/GlobalActionSetting.java: -------------------------------------------------------------------------------- 1 | package de.spiritcroc.modular_remote; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.Log; 6 | 7 | // todo fix edit connection when disabled 8 | public class GlobalActionSetting implements TcpConnectionManager.TcpUpdateInterface { 9 | 10 | private static final String SEP = Util.RK_ATTRIBUTE_SEPARATOR; 11 | private static final String LOG_TAG = GlobalActionSetting.class.getSimpleName(); 12 | 13 | private String ip; 14 | private TcpConnectionManager.ReceiverType type; 15 | private String clickCommand; 16 | 17 | private TcpConnectionManager.TcpConnection connection; 18 | 19 | public GlobalActionSetting(String ip, TcpConnectionManager.ReceiverType type, 20 | String clickCommand, TcpConnectionManager connectionManager) { 21 | this.ip = ip; 22 | this.type = type; 23 | this.clickCommand = clickCommand; 24 | if (connectionManager != null) { 25 | // Register for connection updates 26 | getConnection(connectionManager); 27 | } 28 | } 29 | 30 | public String getRecreationKey() { 31 | return Util.fixRecreationKey(ip + SEP + type.toString() + SEP + clickCommand + SEP, SEP); 32 | } 33 | 34 | public static GlobalActionSetting recoverFromRecreationKey(String key, TcpConnectionManager connectionManager) { 35 | if (!TextUtils.isEmpty(key)) { 36 | try { 37 | String[] args = Util.split(key, SEP, 0); 38 | String ip = args[0]; 39 | TcpConnectionManager.ReceiverType type = 40 | TcpConnectionManager.ReceiverType.valueOf(args[1]); 41 | String clickCommand = args[2]; 42 | return new GlobalActionSetting(ip, type, clickCommand, connectionManager); 43 | } catch (Exception e) { 44 | Log.w(LOG_TAG, "recoverFromRecreationKey: illegal key: " + key); 45 | } 46 | } 47 | // Return default 48 | return new GlobalActionSetting("", TcpConnectionManager.ReceiverType.UNSPECIFIED, "", connectionManager); 49 | } 50 | 51 | public String getIp() { 52 | return ip; 53 | } 54 | 55 | public TcpConnectionManager.ReceiverType getType() { 56 | return type; 57 | } 58 | 59 | public String getClickCommand() { 60 | return clickCommand; 61 | } 62 | 63 | public boolean isVoid() { 64 | return TextUtils.isEmpty(clickCommand) || TextUtils.isEmpty(ip); 65 | } 66 | 67 | public boolean isConnected() { 68 | return !isVoid() && connection != null && connection.isConnected(); 69 | } 70 | 71 | public String getSummary(Context context) { 72 | return TcpConnectionManager.getInstance(context.getApplicationContext()) 73 | .getCommandNameFromResource(context.getResources(), null, type, clickCommand, null); 74 | } 75 | 76 | public TcpConnectionManager.TcpConnection getConnection(TcpConnectionManager manager) { 77 | if (connection == null) { 78 | connection = manager.requireConnection(this); 79 | } 80 | return connection; 81 | } 82 | 83 | public void closeConnection() { 84 | if (connection != null) { 85 | connection.removeListener(this); 86 | connection = null; 87 | } 88 | } 89 | 90 | public boolean sendCommand(Context context) { 91 | if (isVoid()) { 92 | return false; 93 | } else { 94 | getConnection(TcpConnectionManager.getInstance(context)) 95 | .sendRawCommand(clickCommand); 96 | return connection.isConnected(); 97 | } 98 | } 99 | 100 | @Override 101 | public void update(TcpInformation information) {} 102 | 103 | @Override 104 | public void setConnectionValues(String ip, TcpConnectionManager.ReceiverType type) { 105 | this.ip = ip; 106 | this.type = type; 107 | // todo GlobalActionHandler.updateSetting(this); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/ReceiverIpSelectorUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | public interface ReceiverIpSelectorUser { 22 | void setReceiverType(TcpConnectionManager.ReceiverType receiverType); 23 | void resumeDismiss();//resume stuff that waits for OverwriteTypeDialog, don't forget to dismiss! 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.os.Message; 24 | import android.view.MenuItem; 25 | 26 | import androidx.appcompat.app.ActionBar; 27 | import androidx.preference.Preference; 28 | import androidx.preference.PreferenceScreen; 29 | 30 | public class SettingsActivity extends BaseActivity { 31 | 32 | private CustomPreferenceFragment preferenceFragment; 33 | 34 | private static final String EXTRA_PREFERENCE_FRAGMENT = 35 | "de.spiritcroc.modular_remote.extra.preference_fragment"; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | String preferenceKey = getIntent().getStringExtra(EXTRA_PREFERENCE_FRAGMENT); 41 | preferenceFragment = getNewPreferenceFragment(preferenceKey); 42 | getSupportFragmentManager().beginTransaction().replace(android.R.id.content, preferenceFragment) 43 | .commit(); 44 | 45 | ActionBar actionBar = getSupportActionBar(); 46 | if (actionBar != null) { 47 | actionBar.setDisplayHomeAsUpEnabled(true); 48 | } 49 | } 50 | 51 | @Override 52 | protected void onResume() { 53 | super.onResume(); 54 | 55 | ActionBar actionBar = getSupportActionBar(); 56 | if (actionBar != null) { 57 | actionBar.setTitle(preferenceFragment.getPreferenceScreen().getTitle()); 58 | } 59 | } 60 | 61 | @Override 62 | public boolean onOptionsItemSelected(MenuItem item) { 63 | switch (item.getItemId()) { 64 | case android.R.id.home: 65 | finish(); 66 | return true; 67 | default: 68 | return super.onOptionsItemSelected(item); 69 | } 70 | } 71 | 72 | public boolean onPreferenceClick(Preference preference) { 73 | if (preference instanceof PreferenceScreen) { 74 | String fragmentClass = preference.getFragment(); 75 | if (fragmentClass != null) { 76 | startActivity(new Intent(this, SettingsActivity.class) 77 | .putExtra(EXTRA_PREFERENCE_FRAGMENT, fragmentClass)); 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | private CustomPreferenceFragment getNewPreferenceFragment(String preferenceFragment) { 85 | if (preferenceFragment != null) { 86 | try { 87 | return (CustomPreferenceFragment) Class.forName(preferenceFragment).newInstance(); 88 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | 89 | ClassCastException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | return new SettingsFragment(); 94 | } 95 | 96 | @Override 97 | protected void onGlobalKeyMessage(Message message) { 98 | preferenceFragment.onGlobalKeyMessage(message); 99 | super.onGlobalKeyMessage(message); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/SettingsMoreSpaceFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.SharedPreferences; 22 | import android.os.Bundle; 23 | 24 | import androidx.preference.CheckBoxPreference; 25 | import androidx.preference.EditTextPreference; 26 | 27 | public class SettingsMoreSpaceFragment extends CustomPreferenceFragment 28 | implements SharedPreferences.OnSharedPreferenceChangeListener { 29 | 30 | private CheckBoxPreference fullscreenPreference; 31 | private CheckBoxPreference hideNavBarPreference; 32 | private EditTextPreference systemUiTimeoutPreference; 33 | 34 | @Override 35 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 36 | addPreferencesFromResource(R.xml.preferences_more_space); 37 | 38 | systemUiTimeoutPreference = 39 | (EditTextPreference) findPreference(Preferences.SYSTEM_UI_TIMEOUT); 40 | fullscreenPreference = (CheckBoxPreference) findPreference(Preferences.FULLSCREEN); 41 | hideNavBarPreference = 42 | (CheckBoxPreference) findPreference(Preferences.HIDE_NAVIGATION_BAR); 43 | } 44 | 45 | private void init() { 46 | setSystemUiTimeoutSummary(); 47 | setSystemUiTimeoutEnabled(); 48 | } 49 | 50 | @Override 51 | public void onResume() { 52 | super.onResume(); 53 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 54 | init(); 55 | } 56 | 57 | @Override 58 | public void onPause() { 59 | super.onPause(); 60 | getPreferenceScreen().getSharedPreferences() 61 | .unregisterOnSharedPreferenceChangeListener(this); 62 | } 63 | 64 | @Override 65 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 66 | if (Preferences.SYSTEM_UI_TIMEOUT.equals(key)) { 67 | setSystemUiTimeoutSummary(); 68 | } else if (Preferences.FULLSCREEN.equals(key)) { 69 | setSystemUiTimeoutEnabled(); 70 | } else if (Preferences.HIDE_NAVIGATION_BAR.equals(key)) { 71 | setSystemUiTimeoutEnabled(); 72 | } 73 | } 74 | 75 | private void setSystemUiTimeoutSummary() { 76 | int value = correctInteger(getPreferenceManager().getSharedPreferences(), 77 | Preferences.SYSTEM_UI_TIMEOUT, systemUiTimeoutPreference.getText(), 3); 78 | systemUiTimeoutPreference.setSummary(getResources() 79 | .getQuantityString(R.plurals.pref_system_ui_timeout, value, value)); 80 | } 81 | 82 | private void setSystemUiTimeoutEnabled() { 83 | systemUiTimeoutPreference.setEnabled(fullscreenPreference.isChecked() || 84 | hideNavBarPreference.isChecked()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/TimeSingleton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.os.Handler; 22 | import android.util.Log; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Calendar; 26 | 27 | public class TimeSingleton { 28 | private static final String LOG_TAG = TimeSingleton.class.getSimpleName(); 29 | private static final boolean DEBUG = false; 30 | 31 | private static TimeSingleton instance; 32 | private int updateInterval; 33 | private ArrayList timeListeners; 34 | private Handler timeHandler; 35 | private String time; 36 | 37 | private TimeSingleton(int updateInterval) { 38 | setUpdateInterval(updateInterval); 39 | timeHandler = new Handler(); 40 | timeListeners = new ArrayList<>(); 41 | } 42 | 43 | public static TimeSingleton getInstance(int updateInterval) { 44 | if (instance == null) { 45 | instance = new TimeSingleton(updateInterval); 46 | } else { 47 | instance.setUpdateInterval(updateInterval); 48 | } 49 | return instance; 50 | } 51 | public static TimeSingleton getInstance() {// Do not overwrite updateInterval 52 | if (instance == null) { 53 | instance = new TimeSingleton(500);// Default 54 | } 55 | return instance; 56 | } 57 | private void setUpdateInterval(int updateInterval) { 58 | this.updateInterval = updateInterval; 59 | } 60 | 61 | private void setTime(String time) { 62 | for (int i = 0; i < timeListeners.size(); i++) { 63 | timeListeners.get(i).setTime(time); 64 | } 65 | } 66 | 67 | public void registerListener(TimeListener listener) { 68 | if (!timeListeners.contains(listener)) { 69 | if (timeListeners.isEmpty()) { 70 | onResume(); 71 | } 72 | timeListeners.add(listener); 73 | listener.setTime(time); 74 | } 75 | } 76 | public void unregisterListener(TimeListener listener) { 77 | listener.setTime(""); 78 | timeListeners.remove(listener); 79 | if (timeListeners.isEmpty()) { 80 | onPause(); 81 | } 82 | } 83 | 84 | private void onResume() { 85 | timeHandler.postDelayed(timeUpdateTask, updateInterval); 86 | } 87 | private void onPause() { 88 | timeHandler.removeCallbacks(timeUpdateTask); 89 | } 90 | 91 | private Runnable timeUpdateTask = new Runnable() { 92 | @Override 93 | public void run() { 94 | if (updateInterval > 0) { 95 | Calendar calendar = Calendar.getInstance(); 96 | int hour = calendar.get(Calendar.HOUR_OF_DAY); 97 | int minute = calendar.get(Calendar.MINUTE); 98 | int second = calendar.get(Calendar.SECOND); 99 | time = hour + ":"; 100 | if (minute < 10) { 101 | time += "0"; 102 | } 103 | time += minute + ":"; 104 | if (second < 10) { 105 | time += "0"; 106 | } 107 | time += second + ""; 108 | if (DEBUG) Log.v(LOG_TAG, "Update time for " + timeListeners.size() + " listeners"); 109 | setTime(time); 110 | timeHandler.postDelayed(this, updateInterval); 111 | } else { 112 | setTime(""); 113 | } 114 | } 115 | }; 116 | 117 | public interface TimeListener { 118 | void setTime(String time); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/TouchOverlay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote; 20 | 21 | import android.content.Context; 22 | import android.view.MotionEvent; 23 | import android.widget.FrameLayout; 24 | 25 | /** 26 | * An overlay to receive all touches and forward them to the resizeFrame 27 | */ 28 | public class TouchOverlay extends FrameLayout { 29 | ResizeFrame resizeFrame; 30 | 31 | public TouchOverlay(Context context, ResizeFrame resizeFrame) { 32 | super(context); 33 | this.resizeFrame = resizeFrame; 34 | } 35 | 36 | @Override 37 | public boolean onTouchEvent(MotionEvent event) { 38 | return resizeFrame.handleTouchEvent(this, event); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/AboutDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.view.View; 26 | import android.widget.TextView; 27 | 28 | import androidx.appcompat.app.AlertDialog; 29 | import androidx.fragment.app.DialogFragment; 30 | 31 | import java.text.SimpleDateFormat; 32 | import java.util.Date; 33 | 34 | import de.spiritcroc.modular_remote.BuildConfig; 35 | import de.spiritcroc.modular_remote.R; 36 | import de.spiritcroc.modular_remote.Util; 37 | 38 | public class AboutDialog extends DialogFragment { 39 | private static final String LOG_TAG = AboutDialog.class.getSimpleName(); 40 | 41 | @Override 42 | public Dialog onCreateDialog(Bundle savedInstanceState) { 43 | Activity activity = getActivity(); 44 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 45 | View view = activity.getLayoutInflater().inflate(R.layout.dialog_about, null); 46 | String text; 47 | if (BuildConfig.DEBUG) { 48 | text = getString(R.string.dialog_about_app_debug_version, 49 | BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, BuildConfig.GIT_DESCRIBE, 50 | SimpleDateFormat.getDateTimeInstance().format( 51 | new Date(BuildConfig.TIMESTAMP))); 52 | } else { 53 | text = getString(R.string.dialog_about_app_version, BuildConfig.VERSION_NAME, 54 | BuildConfig.VERSION_CODE); 55 | } 56 | ((TextView) view.findViewById(R.id.version_view)).setText(text); 57 | builder.setView(Util.scrollView(view)) 58 | .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { 59 | @Override 60 | public void onClick(DialogInterface dialog, int which) { 61 | // Only close dialog 62 | } 63 | }) 64 | .setNeutralButton(R.string.dialog_greeting, new DialogInterface.OnClickListener() { 65 | @Override 66 | public void onClick(DialogInterface dialog, int which) { 67 | new GreetingDialog().show(getFragmentManager(), "GreetingDialog"); 68 | } 69 | }); 70 | 71 | return builder.create(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/CommandInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | public interface CommandInterface { 22 | void setCommand(int id, String command); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/CustomDialogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.content.DialogInterface; 22 | 23 | import androidx.fragment.app.DialogFragment; 24 | 25 | public abstract class CustomDialogFragment extends DialogFragment { 26 | private OnDismissListener onDismissListener; 27 | 28 | public CustomDialogFragment setOnDismissListener(OnDismissListener onDismissListener) { 29 | this.onDismissListener = onDismissListener; 30 | return this; 31 | } 32 | 33 | @Override 34 | public void onDismiss(DialogInterface dialog) { 35 | super.onDismiss(dialog); 36 | if (onDismissListener != null) { 37 | onDismissListener.onDismiss(); 38 | } 39 | } 40 | 41 | public interface OnDismissListener { 42 | void onDismiss(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/GreetingDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.content.SharedPreferences; 25 | import android.os.Bundle; 26 | import android.preference.PreferenceManager; 27 | import android.text.method.LinkMovementMethod; 28 | import android.util.TypedValue; 29 | import android.widget.TextView; 30 | 31 | import androidx.appcompat.app.AlertDialog; 32 | import androidx.fragment.app.DialogFragment; 33 | 34 | import de.spiritcroc.modular_remote.Preferences; 35 | import de.spiritcroc.modular_remote.R; 36 | import de.spiritcroc.modular_remote.Util; 37 | 38 | public class GreetingDialog extends DialogFragment { 39 | 40 | // Increment if content significantly changes and should be seen again 41 | public static int VERSION = 1; 42 | 43 | @Override 44 | public Dialog onCreateDialog(Bundle savedInstanceState) { 45 | Activity activity = getActivity(); 46 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 47 | TextView view = new TextView(getActivity()); 48 | int padding = getResources().getDimensionPixelSize(R.dimen.dialog_content_padding); 49 | view.setPadding(padding, padding, padding, padding); 50 | view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); 51 | view.setText(R.string.dialog_greeting_message); 52 | view.setMovementMethod(LinkMovementMethod.getInstance()); 53 | builder.setTitle(R.string.dialog_greeting) 54 | .setView(Util.scrollView(view)) 55 | .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { 56 | @Override 57 | public void onClick(DialogInterface dialog, int which) { 58 | SharedPreferences preferences = 59 | PreferenceManager.getDefaultSharedPreferences(getActivity()); 60 | preferences.edit().putInt(Preferences.SEEN_GREETING_VERSION, VERSION) 61 | .apply(); 62 | if (SetupGridSizeDialog.shouldShow(preferences)) { 63 | new SetupGridSizeDialog() 64 | .show(getFragmentManager(), "SetupGridSizeDialog"); 65 | } 66 | } 67 | }); 68 | 69 | return builder.create(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/HideSubmenuItemsDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.AlertDialog; 27 | import androidx.fragment.app.DialogFragment; 28 | 29 | import de.spiritcroc.modular_remote.R; 30 | import de.spiritcroc.modular_remote.TcpConnectionManager; 31 | import de.spiritcroc.modular_remote.TcpInformation; 32 | 33 | public class HideSubmenuItemsDialog extends DialogFragment { 34 | private TcpConnectionManager.TcpConnection connection; 35 | private int submenu; 36 | private String submenuName; 37 | 38 | public HideSubmenuItemsDialog setValues(TcpConnectionManager.TcpConnection connection, 39 | int submenu, String submenuName) { 40 | this.connection = connection; 41 | this.submenu = submenu; 42 | this.submenuName = submenuName; 43 | return this; 44 | } 45 | 46 | @Override 47 | public Dialog onCreateDialog(Bundle savedInstanceState) { 48 | final Activity activity = getActivity(); 49 | final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 50 | 51 | final TcpConnectionManager.TcpConnection.CustomizedMenu customizedMenu = 52 | connection.requireCustomizedMenu(submenu, activity); 53 | final boolean[] selection = customizedMenu.getHidden(); 54 | 55 | return builder.setTitle(getString(R.string.dialog_hide) + submenuName) 56 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | // Only close dialog 60 | } 61 | }) 62 | .setNeutralButton(R.string.dialog_reset, new DialogInterface.OnClickListener() { 63 | @Override 64 | public void onClick(DialogInterface dialog, int which) { 65 | customizedMenu.resetHidden(); 66 | connection.updateListeners(new TcpInformation( 67 | TcpInformation.InformationType.UPDATE_MENU, 68 | customizedMenu.getMenuValue())); 69 | 70 | } 71 | }) 72 | .setPositiveButton(R.string.dialog_hide_selected, 73 | new DialogInterface.OnClickListener() { 74 | @Override 75 | public void onClick(DialogInterface dialog, int which) { 76 | customizedMenu.hidden = selection; 77 | connection.updateListeners(new TcpInformation( 78 | TcpInformation.InformationType.UPDATE_MENU, 79 | customizedMenu.getMenuValue())); 80 | } 81 | }) 82 | .setMultiChoiceItems(customizedMenu.names, selection, 83 | new DialogInterface.OnMultiChoiceClickListener() { 84 | @Override 85 | public void onClick(DialogInterface dialog, int which, 86 | boolean isChecked) { 87 | selection[which] = isChecked; 88 | } 89 | }).create(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/OverlapWarningDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.util.Log; 26 | 27 | import androidx.appcompat.app.AlertDialog; 28 | import androidx.fragment.app.DialogFragment; 29 | 30 | import de.spiritcroc.modular_remote.MainActivity; 31 | import de.spiritcroc.modular_remote.R; 32 | 33 | public class OverlapWarningDialog extends DialogFragment { 34 | private static String LOG_TAG = OverlapWarningDialog.class.getSimpleName(); 35 | 36 | private MainActivity callback; 37 | 38 | @Override 39 | public Dialog onCreateDialog(Bundle savedInstanceState) { 40 | Activity activity = getActivity(); 41 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 42 | builder.setTitle(R.string.dialog_warning) 43 | .setMessage(R.string.dialog_overlapping_fragments_warning) 44 | .setPositiveButton(R.string.dialog_change, new DialogInterface.OnClickListener() { 45 | @Override 46 | public void onClick(DialogInterface dialog, int which) { 47 | if (callback == null) { 48 | Log.e(LOG_TAG, "callback == null"); 49 | } else { 50 | callback.enterEditMode(); 51 | } 52 | } 53 | }) 54 | .setNegativeButton(R.string.dialog_ignore, new DialogInterface.OnClickListener() { 55 | @Override 56 | public void onClick(DialogInterface dialog, int which) { 57 | if (callback == null) { 58 | Log.e(LOG_TAG, "callback == null"); 59 | } else { 60 | callback.exitEditMode(); 61 | } 62 | } 63 | }); 64 | 65 | return builder.create(); 66 | } 67 | 68 | public OverlapWarningDialog setCallback(MainActivity callback) { 69 | this.callback = callback; 70 | return this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/OverwriteTypeDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.AlertDialog; 27 | import androidx.fragment.app.DialogFragment; 28 | 29 | import de.spiritcroc.modular_remote.R; 30 | import de.spiritcroc.modular_remote.ReceiverIpSelectorUser; 31 | import de.spiritcroc.modular_remote.TcpConnectionManager; 32 | 33 | public class OverwriteTypeDialog extends DialogFragment { 34 | private TcpConnectionManager.TcpConnection tcpConnection; 35 | private TcpConnectionManager.ReceiverType receiverType; 36 | private ReceiverIpSelectorUser waiter; 37 | 38 | @Override 39 | public Dialog onCreateDialog(Bundle savedInstanceState){ 40 | final Activity activity = getActivity(); 41 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 42 | 43 | String message = getString(R.string.dialog_overwrite_receiver_type_message, 44 | TcpConnectionManager.getReceiverTypeDisplayString( 45 | getResources(), tcpConnection.getType()), 46 | TcpConnectionManager.getReceiverTypeDisplayString(getResources(), receiverType)); 47 | 48 | return builder.setTitle(R.string.dialog_overwrite_receiver_type) 49 | .setMessage(message) 50 | .setNegativeButton(R.string.dialog_no, new DialogInterface.OnClickListener() { 51 | @Override 52 | public void onClick(DialogInterface dialog, int which) { 53 | //only dismiss this dialog 54 | } 55 | }).setPositiveButton(R.string.dialog_yes, new DialogInterface.OnClickListener() { 56 | @Override 57 | public void onClick(DialogInterface dialog, int which) { 58 | tcpConnection.setType(receiverType); 59 | waiter.resumeDismiss(); 60 | } 61 | }).create(); 62 | } 63 | 64 | public OverwriteTypeDialog setValues(TcpConnectionManager.TcpConnection tcpConnection, 65 | TcpConnectionManager.ReceiverType receiverType, 66 | ReceiverIpSelectorUser waiter) { 67 | this.tcpConnection = tcpConnection; 68 | this.receiverType = receiverType; 69 | this.waiter = waiter; 70 | return this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/RemoveConnectionDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.util.Log; 26 | 27 | import androidx.appcompat.app.AlertDialog; 28 | import androidx.fragment.app.DialogFragment; 29 | 30 | import de.spiritcroc.modular_remote.R; 31 | import de.spiritcroc.modular_remote.TcpConnectionManager; 32 | 33 | public class RemoveConnectionDialog extends DialogFragment { 34 | private final static String LOG_TAG = RemoveConnectionDialog.class.getSimpleName(); 35 | 36 | private TcpConnectionManager.TcpConnection connection; 37 | 38 | public RemoveConnectionDialog setConnection(TcpConnectionManager.TcpConnection connection) { 39 | this.connection = connection; 40 | return this; 41 | } 42 | 43 | @Override 44 | public Dialog onCreateDialog(Bundle savedInstanceState) { 45 | final Activity activity = getActivity(); 46 | AlertDialog.Builder builder = new AlertDialog.Builder(activity); 47 | 48 | String ip; 49 | if (connection == null) { 50 | Log.e(LOG_TAG, "connection == null"); 51 | dismiss(); 52 | ip = ""; 53 | } else { 54 | ip = connection.getIp(); 55 | } 56 | 57 | return builder.setTitle(R.string.dialog_remove_connection) 58 | .setMessage(getString(R.string.dialog_remove_connection_message, ip)) 59 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 60 | @Override 61 | public void onClick(DialogInterface dialog, int which) { 62 | // Only dismiss 63 | } 64 | }).setPositiveButton(R.string.dialog_remove, new DialogInterface.OnClickListener() { 65 | @Override 66 | public void onClick(DialogInterface dialog, int which) { 67 | if (connection != null) { 68 | TcpConnectionManager.getInstance(getActivity().getApplicationContext()) 69 | .removeConnection(connection); 70 | } 71 | } 72 | }).create(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/RemoveContainerDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.util.Log; 26 | 27 | import androidx.appcompat.app.AlertDialog; 28 | import androidx.fragment.app.DialogFragment; 29 | 30 | import de.spiritcroc.modular_remote.MainActivity; 31 | import de.spiritcroc.modular_remote.R; 32 | import de.spiritcroc.modular_remote.modules.ModuleFragment; 33 | import de.spiritcroc.modular_remote.modules.PageContainerFragment; 34 | 35 | public class RemoveContainerDialog extends DialogFragment { 36 | private static final String LOG_TAG = RemoveContainerDialog.class.getSimpleName(); 37 | 38 | private ModuleFragment fragment; 39 | 40 | public RemoveContainerDialog setFragment(ModuleFragment fragment) { 41 | this.fragment = fragment; 42 | return this; 43 | } 44 | 45 | @Override 46 | public Dialog onCreateDialog(Bundle savedInstanceState) { 47 | return new AlertDialog.Builder(getActivity()) 48 | .setMessage(getString(R.string.dialog_remove_container, fragment.getReadableName())) 49 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 50 | @Override 51 | public void onClick(DialogInterface dialog, int which) { 52 | // Only close dialog 53 | } 54 | }) 55 | .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { 56 | @Override 57 | public void onClick(DialogInterface dialog, int which) { 58 | if (fragment instanceof PageContainerFragment) { 59 | Activity activity = getActivity(); 60 | if (activity instanceof MainActivity) { 61 | ((MainActivity) activity) 62 | .removePage((PageContainerFragment) fragment, true); 63 | } else { 64 | Log.e(LOG_TAG, "!(getActivity() instanceof MainActivity)"); 65 | } 66 | } else { 67 | fragment.getParent().removeFragment(fragment, true); 68 | } 69 | } 70 | }) 71 | .create(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/SelectConnectionDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.AlertDialog; 27 | import androidx.fragment.app.DialogFragment; 28 | 29 | import de.spiritcroc.modular_remote.R; 30 | import de.spiritcroc.modular_remote.TcpConnectionManager; 31 | 32 | public class SelectConnectionDialog extends DialogFragment { 33 | private int selection = 0; 34 | 35 | @Override 36 | public Dialog onCreateDialog(Bundle savedInstanceState) { 37 | final Activity activity = getActivity(); 38 | final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 39 | 40 | final String[] items = TcpConnectionManager.getInstance(getActivity() 41 | .getApplicationContext()).getConnectionSuggestions(); 42 | 43 | return builder.setTitle(R.string.dialog_select_connection) 44 | .setSingleChoiceItems(items, selection, new DialogInterface.OnClickListener() { 45 | @Override 46 | public void onClick(DialogInterface dialog, int which) { 47 | selection = which; 48 | } 49 | }) 50 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 51 | @Override 52 | public void onClick(DialogInterface dialog, int which) { 53 | // Only close dialog 54 | } 55 | }) 56 | .setPositiveButton(R.string.dialog_edit, new DialogInterface.OnClickListener() { 57 | @Override 58 | public void onClick(DialogInterface dialog, int which) { 59 | new EditConnectionDialog().setConnection(TcpConnectionManager 60 | .getInstance(getActivity().getApplicationContext()) 61 | .getTcpConnection(items[selection])) 62 | .show(getFragmentManager(), "EditConnectionDialog"); 63 | } 64 | }) 65 | .setNeutralButton(R.string.dialog_remove, new DialogInterface.OnClickListener() { 66 | @Override 67 | public void onClick(DialogInterface dialog, int which) { 68 | new RemoveConnectionDialog().setConnection(TcpConnectionManager 69 | .getInstance(getActivity().getApplicationContext()) 70 | .getTcpConnection(items[selection])) 71 | .show(getFragmentManager(), "RemoveConnectionDialog"); 72 | } 73 | }) 74 | .create(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/SelectEditSubmenuDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.AlertDialog; 27 | import androidx.fragment.app.DialogFragment; 28 | 29 | import java.util.ArrayList; 30 | 31 | import de.spiritcroc.modular_remote.R; 32 | import de.spiritcroc.modular_remote.TcpConnectionManager; 33 | 34 | public class SelectEditSubmenuDialog extends DialogFragment { 35 | private TcpConnectionManager.TcpConnection connection; 36 | private int selection = 0; 37 | 38 | @Override 39 | public Dialog onCreateDialog(Bundle savedInstanceState) { 40 | final Activity activity = getActivity(); 41 | final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 42 | 43 | final String[] allNames = TcpConnectionManager.getCustomizableSubmenuNames(getResources(), 44 | connection.getType()); 45 | final int[] allSubmenus = 46 | TcpConnectionManager.getCustomizableSubmenus(getResources(), connection.getType()), 47 | allValues = TcpConnectionManager.getCustomizableSubmenuValues(getResources(), 48 | connection.getType()); 49 | final ArrayList names = new ArrayList<>(); 50 | final ArrayList submenus = new ArrayList<>(); 51 | for (int i = 0; i < allValues.length; i++) { 52 | if ((allValues[i] & TcpConnectionManager.CUSTOMIZABLE_SUBMENU_FLAG_HIDE_ITEMS) == 53 | TcpConnectionManager.CUSTOMIZABLE_SUBMENU_FLAG_HIDE_ITEMS) { 54 | names.add(allNames[i]); 55 | submenus.add(allSubmenus[i]); 56 | } 57 | } 58 | 59 | 60 | return builder.setTitle(R.string.dialog_select_submenu) 61 | .setSingleChoiceItems(names.toArray(new String[names.size()]), selection, 62 | new DialogInterface.OnClickListener() { 63 | @Override 64 | public void onClick(DialogInterface dialog, int which) { 65 | selection = which; 66 | } 67 | }) 68 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 69 | @Override 70 | public void onClick(DialogInterface dialog, int which) { 71 | //only close dialog 72 | } 73 | }) 74 | .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { 75 | @Override 76 | public void onClick(DialogInterface dialog, int which) { 77 | new HideSubmenuItemsDialog().setValues(connection, submenus.get(selection), 78 | names.get(selection)) 79 | .show(getFragmentManager(), "HideSubmenuItemsDialog"); 80 | } 81 | }) 82 | .create(); 83 | } 84 | 85 | public SelectEditSubmenuDialog setConnection(TcpConnectionManager.TcpConnection connection) { 86 | this.connection = connection; 87 | return this; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/SortFragmentsDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.text.Spannable; 26 | import android.text.SpannableString; 27 | import android.view.View; 28 | import android.widget.ArrayAdapter; 29 | 30 | import androidx.appcompat.app.AlertDialog; 31 | 32 | import java.util.ArrayList; 33 | 34 | import de.spiritcroc.modular_remote.R; 35 | import de.spiritcroc.modular_remote.dslv.DragSortListView; 36 | import de.spiritcroc.modular_remote.modules.Container; 37 | import de.spiritcroc.modular_remote.modules.ModuleFragment; 38 | 39 | public class SortFragmentsDialog extends CustomDialogFragment { 40 | 41 | private Container container; 42 | private ModuleFragment[] initFragments; 43 | private ArrayList sortFragments = new ArrayList<>(); 44 | private ArrayList sortFragmentNames = new ArrayList<>(); 45 | private ArrayAdapter arrayAdapter; 46 | 47 | @Override 48 | public Dialog onCreateDialog(Bundle savedInstanceState){ 49 | final Activity activity = getActivity(); 50 | final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 51 | 52 | View view = activity.getLayoutInflater().inflate(R.layout.dialog_sort, null); 53 | 54 | DragSortListView dslv = (DragSortListView) view.findViewById(R.id.drag_sort_list); 55 | 56 | initFragments = container.getFragments(); 57 | for (ModuleFragment fragment: initFragments) { 58 | sortFragments.add(fragment); 59 | sortFragmentNames.add(fragment instanceof Container ? 60 | ((Container) fragment).getContentReadableName(null) : 61 | new SpannableString(fragment.getReadableName())); 62 | } 63 | 64 | arrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, 65 | sortFragmentNames); 66 | dslv.setAdapter(arrayAdapter); 67 | dslv.setDropListener(new DragSortListView.DropListener() { 68 | @Override 69 | public void drop(int from, int to) { 70 | sortFragments.add(to, sortFragments.remove(from)); 71 | sortFragmentNames.add(to, sortFragmentNames.remove(from)); 72 | arrayAdapter.notifyDataSetChanged(); 73 | } 74 | }); 75 | 76 | return builder.setTitle(R.string.dialog_sort_fragments) 77 | .setPositiveButton(R.string.dialog_apply, new DialogInterface.OnClickListener() { 78 | @Override 79 | public void onClick(DialogInterface dialog, int which) { 80 | // Remove 81 | for (int i = 0; i < sortFragments.size(); i++) { 82 | container.removeFragment(sortFragments.get(i), false); 83 | } 84 | // Re-add in new order 85 | for (int i = 0; i < sortFragments.size(); i++) { 86 | container.addFragment(sortFragments.get(i), true); 87 | } 88 | } 89 | }) 90 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 91 | @Override 92 | public void onClick(DialogInterface dialog, int which) { 93 | // Close dialog 94 | } 95 | }) 96 | .setView(view).create(); 97 | } 98 | 99 | public SortFragmentsDialog setContainer(Container container) { 100 | this.container = container; 101 | return this; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dialogs/SortPagesDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.dialogs; 20 | 21 | import android.app.Activity; 22 | import android.app.Dialog; 23 | import android.content.DialogInterface; 24 | import android.os.Bundle; 25 | import android.util.Log; 26 | import android.view.View; 27 | import android.widget.ArrayAdapter; 28 | 29 | import androidx.appcompat.app.AlertDialog; 30 | import androidx.fragment.app.DialogFragment; 31 | 32 | import java.util.ArrayList; 33 | 34 | import de.spiritcroc.modular_remote.MainActivity; 35 | import de.spiritcroc.modular_remote.R; 36 | import de.spiritcroc.modular_remote.dslv.DragSortListView; 37 | import de.spiritcroc.modular_remote.modules.PageContainerFragment; 38 | 39 | public class SortPagesDialog extends DialogFragment { 40 | private static final String LOG_TAG = SortPagesDialog.class.getSimpleName(); 41 | 42 | private ArrayList sortPageNames = new ArrayList<>(); 43 | private ArrayList sortOrder = new ArrayList<>(); 44 | private ArrayAdapter arrayAdapter; 45 | 46 | @Override 47 | public Dialog onCreateDialog(Bundle savedInstanceState){ 48 | final Activity activity = getActivity(); 49 | final AlertDialog.Builder builder = new AlertDialog.Builder(activity); 50 | 51 | View view = activity.getLayoutInflater().inflate(R.layout.dialog_sort, null); 52 | 53 | if (activity instanceof MainActivity) { 54 | MainActivity mainActivity = (MainActivity) activity; 55 | 56 | DragSortListView dslv = (DragSortListView) view.findViewById(R.id.drag_sort_list); 57 | 58 | PageContainerFragment[] initPages = mainActivity.getPages(); 59 | 60 | for (PageContainerFragment page: initPages) { 61 | sortPageNames.add(page.getName()); 62 | sortOrder.add(sortOrder.size()); 63 | } 64 | 65 | arrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, 66 | sortPageNames); 67 | dslv.setAdapter(arrayAdapter); 68 | dslv.setDropListener(new DragSortListView.DropListener() { 69 | @Override 70 | public void drop(int from, int to) { 71 | sortPageNames.add(to, sortPageNames.remove(from)); 72 | sortOrder.add(to, sortOrder.remove(from)); 73 | arrayAdapter.notifyDataSetChanged(); 74 | } 75 | }); 76 | } else { 77 | Log.e(LOG_TAG, "!(activity instanceof MainActivity)"); 78 | } 79 | 80 | return builder.setTitle(R.string.dialog_sort_pages) 81 | .setPositiveButton(R.string.dialog_apply, new DialogInterface.OnClickListener() { 82 | @Override 83 | public void onClick(DialogInterface dialog, int which) { 84 | if (activity instanceof MainActivity) { 85 | ((MainActivity) activity).orderPages(sortOrder); 86 | } else { 87 | Log.e(LOG_TAG, "!(activity instanceof MainActivity)"); 88 | } 89 | } 90 | }) 91 | .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener() { 92 | @Override 93 | public void onClick(DialogInterface dialog, int which) { 94 | // Close dialog 95 | } 96 | }) 97 | .setView(view).create(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dslv/DragSortItemView.java: -------------------------------------------------------------------------------- 1 | package de.spiritcroc.modular_remote.dslv; 2 | 3 | import android.content.Context; 4 | import android.view.Gravity; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.AbsListView; 8 | 9 | /** 10 | * Lightweight ViewGroup that wraps list items obtained from user's 11 | * ListAdapter. ItemView expects a single child that has a definite 12 | * height (i.e. the child's layout height is not MATCH_PARENT). 13 | * The width of 14 | * ItemView will always match the width of its child (that is, 15 | * the width MeasureSpec given to ItemView is passed directly 16 | * to the child, and the ItemView measured width is set to the 17 | * child's measured width). The height of ItemView can be anything; 18 | * the 19 | * 20 | * 21 | * The purpose of this class is to optimize slide 22 | * shuffle animations. 23 | */ 24 | public class DragSortItemView extends ViewGroup { 25 | 26 | private int mGravity = Gravity.TOP; 27 | 28 | public DragSortItemView(Context context) { 29 | super(context); 30 | 31 | // always init with standard ListView layout params 32 | setLayoutParams(new AbsListView.LayoutParams( 33 | ViewGroup.LayoutParams.FILL_PARENT, 34 | ViewGroup.LayoutParams.WRAP_CONTENT)); 35 | 36 | //setClipChildren(true); 37 | } 38 | 39 | public void setGravity(int gravity) { 40 | mGravity = gravity; 41 | } 42 | 43 | public int getGravity() { 44 | return mGravity; 45 | } 46 | 47 | @Override 48 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 49 | final View child = getChildAt(0); 50 | 51 | if (child == null) { 52 | return; 53 | } 54 | 55 | if (mGravity == Gravity.TOP) { 56 | child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight()); 57 | } else { 58 | child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight()); 59 | } 60 | } 61 | 62 | /** 63 | * 64 | */ 65 | @Override 66 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 67 | 68 | int height = MeasureSpec.getSize(heightMeasureSpec); 69 | int width = MeasureSpec.getSize(widthMeasureSpec); 70 | 71 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 72 | 73 | final View child = getChildAt(0); 74 | if (child == null) { 75 | setMeasuredDimension(0, width); 76 | return; 77 | } 78 | 79 | if (child.isLayoutRequested()) { 80 | // Always let child be as tall as it wants. 81 | measureChild(child, widthMeasureSpec, 82 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 83 | } 84 | 85 | if (heightMode == MeasureSpec.UNSPECIFIED) { 86 | ViewGroup.LayoutParams lp = getLayoutParams(); 87 | 88 | if (lp.height > 0) { 89 | height = lp.height; 90 | } else { 91 | height = child.getMeasuredHeight(); 92 | } 93 | } 94 | 95 | setMeasuredDimension(width, height); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dslv/DragSortItemViewCheckable.java: -------------------------------------------------------------------------------- 1 | package de.spiritcroc.modular_remote.dslv; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.widget.Checkable; 6 | 7 | /** 8 | * Lightweight ViewGroup that wraps list items obtained from user's 9 | * ListAdapter. ItemView expects a single child that has a definite 10 | * height (i.e. the child's layout height is not MATCH_PARENT). 11 | * The width of 12 | * ItemView will always match the width of its child (that is, 13 | * the width MeasureSpec given to ItemView is passed directly 14 | * to the child, and the ItemView measured width is set to the 15 | * child's measured width). The height of ItemView can be anything; 16 | * the 17 | * 18 | * 19 | * The purpose of this class is to optimize slide 20 | * shuffle animations. 21 | */ 22 | public class DragSortItemViewCheckable extends DragSortItemView implements Checkable { 23 | 24 | public DragSortItemViewCheckable(Context context) { 25 | super(context); 26 | } 27 | 28 | @Override 29 | public boolean isChecked() { 30 | View child = getChildAt(0); 31 | if (child instanceof Checkable) 32 | return ((Checkable) child).isChecked(); 33 | else 34 | return false; 35 | } 36 | 37 | @Override 38 | public void setChecked(boolean checked) { 39 | View child = getChildAt(0); 40 | if (child instanceof Checkable) 41 | ((Checkable) child).setChecked(checked); 42 | } 43 | 44 | @Override 45 | public void toggle() { 46 | View child = getChildAt(0); 47 | if (child instanceof Checkable) 48 | ((Checkable) child).toggle(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/dslv/SimpleFloatViewManager.java: -------------------------------------------------------------------------------- 1 | package de.spiritcroc.modular_remote.dslv; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | import android.graphics.Color; 6 | import android.widget.ListView; 7 | import android.widget.ImageView; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | /** 12 | * Simple implementation of the FloatViewManager class. Uses list 13 | * items as they appear in the ListView to create the floating View. 14 | */ 15 | public class SimpleFloatViewManager implements DragSortListView.FloatViewManager { 16 | 17 | private Bitmap mFloatBitmap; 18 | 19 | private ImageView mImageView; 20 | 21 | private int mFloatBGColor = Color.BLACK; 22 | 23 | private ListView mListView; 24 | 25 | public SimpleFloatViewManager(ListView lv) { 26 | mListView = lv; 27 | } 28 | 29 | public void setBackgroundColor(int color) { 30 | mFloatBGColor = color; 31 | } 32 | 33 | /** 34 | * This simple implementation creates a Bitmap copy of the 35 | * list item currently shown at ListView position. 36 | */ 37 | @Override 38 | public View onCreateFloatView(int position) { 39 | // Guaranteed that this will not be null? I think so. Nope, got 40 | // a NullPointerException once... 41 | View v = mListView.getChildAt(position + mListView.getHeaderViewsCount() - mListView.getFirstVisiblePosition()); 42 | 43 | if (v == null) { 44 | return null; 45 | } 46 | 47 | v.setPressed(false); 48 | 49 | // Create a copy of the drawing cache so that it does not get 50 | // recycled by the framework when the list tries to clean up memory 51 | //v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); 52 | v.setDrawingCacheEnabled(true); 53 | mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache()); 54 | v.setDrawingCacheEnabled(false); 55 | 56 | if (mImageView == null) { 57 | mImageView = new ImageView(mListView.getContext()); 58 | } 59 | mImageView.setBackgroundColor(mFloatBGColor); 60 | mImageView.setPadding(0, 0, 0, 0); 61 | mImageView.setImageBitmap(mFloatBitmap); 62 | mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight())); 63 | 64 | return mImageView; 65 | } 66 | 67 | /** 68 | * This does nothing 69 | */ 70 | @Override 71 | public void onDragFloatView(View floatView, Point position, Point touch) { 72 | // do nothing 73 | } 74 | 75 | /** 76 | * Removes the Bitmap from the ImageView created in 77 | * onCreateFloatView() and tells the system to recycle it. 78 | */ 79 | @Override 80 | public void onDestroyFloatView(View floatView) { 81 | ((ImageView) floatView).setImageDrawable(null); 82 | 83 | mFloatBitmap.recycle(); 84 | mFloatBitmap = null; 85 | } 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /app/src/main/java/de/spiritcroc/modular_remote/modules/Container.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 SpiritCroc 3 | * Email: spiritcroc@gmail.com 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | package de.spiritcroc.modular_remote.modules; 20 | 21 | import android.text.Spannable; 22 | 23 | import androidx.annotation.Nullable; 24 | 25 | import de.spiritcroc.modular_remote.ResizeFrame; 26 | 27 | public interface Container { 28 | void addFragment(ModuleFragment fragment, boolean post); 29 | 30 | /** 31 | * @param callOnRemove 32 | * True if fragment will be completely removed, false if fragment will get added to a container 33 | * later 34 | */ 35 | void removeFragment(ModuleFragment fragment, boolean callOnRemove); 36 | int getDepth(); 37 | boolean scrollsX(); 38 | boolean scrollsY(); 39 | /** 40 | * @return 41 | * Scroll offset in x-direction 42 | */ 43 | int getScrollX(); 44 | /** 45 | * @return 46 | * Scroll offset in y-direction 47 | */ 48 | int getScrollY(); 49 | 50 | /** 51 | * If container needs some resizing because of moved content, it can be called in this method 52 | */ 53 | void onContentMoved(); 54 | 55 | /** 56 | * @return 57 | * Util.getAllContainers() 58 | */ 59 | Container[] getAllContainers(); 60 | 61 | /** 62 | * @return 63 | * Container that calls the method and all contained elements 64 | */ 65 | ModuleFragment[] getAllFragments(); 66 | 67 | /** 68 | * @return 69 | * Directly contained elements 70 | */ 71 | ModuleFragment[] getFragments(); 72 | 73 | /** 74 | * @return 75 | * Util.getContainerContentReadableName() 76 | */ 77 | Spannable getContentReadableName(@Nullable String prefix); 78 | 79 | boolean isEmpty(); 80 | int getFragmentCount(); 81 | 82 | String getReadableName(); 83 | 84 | void addResizeFrame(ResizeFrame resizeFrame); 85 | void removeResizeFrame(ResizeFrame resizeFrame); 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_container_drag_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_action_container_drag_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_container_scroll_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_action_container_scroll_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_create_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_create_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_tune_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_tune_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_widget_resize_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/ic_widget_resize_handle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/widget_resize_frame.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/widget_resize_frame.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/widget_resize_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-hdpi/widget_resize_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_container_drag_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_action_container_drag_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_container_scroll_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_action_container_scroll_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_create_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_create_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_tune_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_tune_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_widget_resize_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/ic_widget_resize_handle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/widget_resize_frame.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/widget_resize_frame.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/widget_resize_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-mdpi/widget_resize_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_container_drag_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_action_container_drag_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_container_scroll_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_action_container_scroll_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_create_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_tune_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_tune_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_widget_resize_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/ic_widget_resize_handle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/widget_resize_frame.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/widget_resize_frame.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/widget_resize_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xhdpi/widget_resize_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_container_drag_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_action_container_drag_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_container_scroll_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_action_container_scroll_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_create_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_tune_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_tune_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_widget_resize_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/ic_widget_resize_handle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/widget_resize_frame.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/widget_resize_frame.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/widget_resize_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxhdpi/widget_resize_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_action_container_drag_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_action_container_drag_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_action_container_scroll_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_action_container_scroll_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_create_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_create_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_tune_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_tune_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_widget_resize_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/ic_widget_resize_handle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/widget_resize_frame.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/widget_resize_frame.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/widget_resize_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SpiritCroc/Modular-Remote/6bd35369c66cdadd6699b49acf43a59d29585521/app/src/main/res/drawable-xxxhdpi/widget_resize_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_monochrome.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/abcd_alert_dialog_button_bar_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 25 | 26 | 36 | 37 |