diff --git a/AndroidManifest.xml b/AndroidManifest.xml --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -30,6 +30,7 @@ + + + + + + + + + + + + + + + + android:paddingStart="12dip" + android:id="@+id/list_item_entry"> diff --git a/res/layout/widget_remotecommandplugin.xml b/res/layout/widget_remotecommandplugin.xml new file mode 100644 --- /dev/null +++ b/res/layout/widget_remotecommandplugin.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/widget_remotecommandplugin_dialog.xml b/res/layout/widget_remotecommandplugin_dialog.xml new file mode 100644 --- /dev/null +++ b/res/layout/widget_remotecommandplugin_dialog.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/res/xml/remotecommandplugin_widget.xml b/res/xml/remotecommandplugin_widget.xml new file mode 100644 --- /dev/null +++ b/res/xml/remotecommandplugin_widget.xml @@ -0,0 +1,3 @@ + + diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/CommandEntry.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/CommandEntry.java --- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/CommandEntry.java +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/CommandEntry.java @@ -37,4 +37,8 @@ public String getName() { return title; } + + public String getCommand() { + return subtitle; + } } diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java --- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java @@ -33,6 +33,8 @@ import org.kde.kdeconnect_tp.R; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; public class RunCommandPlugin extends Plugin { @@ -43,6 +45,7 @@ private ArrayList commandList = new ArrayList<>(); private ArrayList callbacks = new ArrayList<>(); + private final ArrayList< CommandEntry > commandItems = new ArrayList<>(); private boolean canAddCommand; @@ -62,6 +65,10 @@ return commandList; } + public ArrayList< CommandEntry > getCommandItems(){ + return commandItems; + } + @Override public String getDisplayName() { return context.getResources().getString(R.string.pref_plugin_runcommand); @@ -89,6 +96,7 @@ if (np.has("commandList")) { commandList.clear(); try { + commandItems.clear(); JSONObject obj = new JSONObject(np.getString("commandList")); Iterator keys = obj.keys(); while (keys.hasNext()) { @@ -96,7 +104,31 @@ JSONObject o = obj.getJSONObject(s); o.put("key", s); commandList.add(o); + + try{ + commandItems.add( + new CommandEntry( + o.getString( "name" ), + o.getString( "command" ), + o.getString( "key" ) + ) + ); + } + catch (JSONException e){ + e.printStackTrace( ); + } } + + Collections.sort( commandItems, new Comparator(){ + @Override + public int compare( CommandEntry lhs, CommandEntry rhs ){ + return lhs.getName( ).compareTo( rhs.getName( ) ); + } + }); + + Intent updateWidget = new Intent( context, RunCommandWidget.class ); + context.sendBroadcast( updateWidget ); + } catch (JSONException e) { e.printStackTrace(); } diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java new file mode 100644 --- /dev/null +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java @@ -0,0 +1,162 @@ +package org.kde.kdeconnect.Plugins.RunCommandPlugin; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.util.Log; +import android.view.View; +import android.widget.RemoteViews; + +import org.kde.kdeconnect.BackgroundService; +import org.kde.kdeconnect.Device; +import org.kde.kdeconnect.Helpers.DeviceHelper; +import org.kde.kdeconnect_tp.R; + +@RequiresApi( api = Build.VERSION_CODES.ICE_CREAM_SANDWICH ) +public class RunCommandWidget extends AppWidgetProvider{ + + public static final String RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION"; + public static final String TARGET_COMMAND = "TARGET_COMMAND"; + public static final String TARGET_DEVICE = "TARGET_DEVICE"; + public static final String SET_CURRENT_DEVICE = "SET_CURRENT_DEVICE"; + + private static Device currentDevice; + + @Override + public void onReceive( Context context, Intent intent ){ + + super.onReceive( context, intent ); + + if( intent != null && intent.getAction( ) != null && intent.getAction( ).equals( RUN_COMMAND_ACTION ) ){ + + final String targetCommand = intent.getStringExtra( TARGET_COMMAND ); + final String targetDevice = intent.getStringExtra( TARGET_DEVICE ); + + BackgroundService.RunCommand( context, new BackgroundService.InstanceCallback( ){ + @Override + public void onServiceStart( BackgroundService service ){ + RunCommandPlugin plugin = service.getDevice( targetDevice ).getPlugin( RunCommandPlugin.class ); + + if( plugin != null ){ + try{ + + plugin.runCommand( targetCommand ); + } + catch( Exception ex ){ + Log.e( "RunCommandWidget", "Error running command", ex ); + } + } + } + } ); + } + else if( intent != null && intent.getAction() != null && intent.getAction().equals( SET_CURRENT_DEVICE )){ + setCurrentDevice( context ); + } + + + final Intent newIntent = new Intent( context, RunCommandWidgetDataProviderService.class ); + context.startService( newIntent ); + updateWidget( context ); + + } + + private void setCurrentDevice( final Context context ){ + Intent popup = new Intent(context, RunCommandWidgetDeviceSelector.class); + popup.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + context.startActivity( popup ); + } + + public void onUpdate( Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds ){ + updateWidget( context ); + } + + private void updateWidget( final Context context ){ + + if( currentDevice == null || !currentDevice.isReachable() ){ + + BackgroundService.RunCommand( context, new BackgroundService.InstanceCallback( ){ + @Override + public void onServiceStart( BackgroundService service ){ + if( service.getDevices().size() > 0 ) + currentDevice = service.getDevices().elements().nextElement(); + + updateWidgetImpl( context ); + } + } ); + } + + updateWidgetImpl( context ); + } + + private void updateWidgetImpl( Context context ){ + + + try{ + + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance( context ); + int[] appWidgetIds = appWidgetManager.getAppWidgetIds( new ComponentName( context, RunCommandWidget.class ) ); + RemoteViews views = new RemoteViews( context.getPackageName( ), R.layout.widget_remotecommandplugin );; + + PendingIntent pendingIntent; + Intent intent; + + intent = new Intent( context, RunCommandWidget.class ); + intent.setAction( SET_CURRENT_DEVICE ); + pendingIntent = PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT ); + views.setOnClickPendingIntent( R.id.runcommandWidgetTitleHeader, pendingIntent ); + + if( currentDevice == null || !currentDevice.isReachable() ){ + views.setTextViewText( R.id.runcommandWidgetTitle, context.getString( R.string.pref_plugin_runcommand ) ); + views.setViewVisibility( R.id.runcommandslist, View.GONE ); + views.setViewVisibility( R.id.not_reachable_message, View.VISIBLE ); + } + else{ + views.setTextViewText( R.id.runcommandWidgetTitle, DeviceHelper.getDeviceName( context ) + "@" + currentDevice.getName( ) ); + views.setViewVisibility( R.id.runcommandslist, View.VISIBLE ); + views.setViewVisibility( R.id.not_reachable_message, View.GONE ); + } + + for( int i = 0; i < appWidgetIds.length; i++ ){ + + intent = new Intent( context, RunCommandWidgetDataProviderService.class ); + intent.putExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[ i ] ); + intent.setData( Uri.parse( intent.toUri( Intent.URI_INTENT_SCHEME ) ) ); + views.setRemoteAdapter( R.id.runcommandslist, intent ); + + intent = new Intent( context, RunCommandWidget.class ); + pendingIntent = PendingIntent.getBroadcast( context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT ); + views.setPendingIntentTemplate( R.id.runcommandslist, pendingIntent ); + + AppWidgetManager.getInstance( context ).updateAppWidget( appWidgetIds[ i ], views ); + } + + } + catch( Exception ex ){ + Log.e( "RunCommandWidget", "Error updating widget", ex ); + } + } + + public static Device getCurrentDevice(){ + return currentDevice; + } + + public static void setCurrentDevice( final Context context, final String DeviceId ){ + BackgroundService.RunCommand( context, new BackgroundService.InstanceCallback( ){ + @Override + public void onServiceStart( BackgroundService service ){ + if( service.getDevices().size() > 0 ){ + currentDevice = service.getDevice( DeviceId ); + Intent updateWidget = new Intent( context, RunCommandWidget.class ); + context.sendBroadcast( updateWidget ); + } + } + } ); + } +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java new file mode 100644 --- /dev/null +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java @@ -0,0 +1,89 @@ +package org.kde.kdeconnect.Plugins.RunCommandPlugin; + +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.view.View; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +import org.kde.kdeconnect_tp.R; + + +@RequiresApi( api = Build.VERSION_CODES.ICE_CREAM_SANDWICH ) +public class RunCommandWidgetDataProvider implements RemoteViewsService.RemoteViewsFactory{ + + private RunCommandPlugin runCommandPlugin; + private Context mContext; + + public RunCommandWidgetDataProvider( Context context, Intent intent ){ + mContext = context; + } + + private boolean checkPlugin(){ + if( RunCommandWidget.getCurrentDevice() == null || !RunCommandWidget.getCurrentDevice().isReachable() ) + return false; + + runCommandPlugin = RunCommandWidget.getCurrentDevice().getPlugin( RunCommandPlugin.class ); + return runCommandPlugin != null; + } + + @Override + public void onCreate( ){ + } + + @Override + public void onDataSetChanged( ){ + + } + + @Override + public void onDestroy( ){ + } + + @Override + public int getCount( ){ + return checkPlugin() ? runCommandPlugin.getCommandItems().size( ) : 0; + } + + @Override + public RemoteViews getViewAt( int i ){ + + RemoteViews remoteView = new RemoteViews( mContext.getPackageName( ), R.layout.list_item_entry ); + CommandEntry listItem = runCommandPlugin.getCommandItems().get( i ); + + final Intent configIntent = new Intent( mContext, RunCommandWidget.class ); + configIntent.setAction( RunCommandWidget.RUN_COMMAND_ACTION ); + configIntent.putExtra( RunCommandWidget.TARGET_COMMAND, listItem.getKey() ); + configIntent.putExtra( RunCommandWidget.TARGET_DEVICE, RunCommandWidget.getCurrentDevice().getDeviceId() ); + + remoteView.setTextViewText( R.id.list_item_entry_title, listItem.getName( ) ); + remoteView.setTextViewText( R.id.list_item_entry_summary, listItem.getCommand() ); + remoteView.setViewVisibility( R.id.list_item_entry_summary, View.VISIBLE ); + + remoteView.setOnClickFillInIntent( R.id.list_item_entry, configIntent ); + + return remoteView; + } + + @Override + public RemoteViews getLoadingView( ){ + return null; + } + + @Override + public int getViewTypeCount( ){ + return 1; + } + + @Override + public long getItemId( int i ){ + return runCommandPlugin.getCommandItems().get( i ).getKey().hashCode( ); + } + + @Override + public boolean hasStableIds( ){ + return false; + } +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java new file mode 100644 --- /dev/null +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java @@ -0,0 +1,16 @@ +package org.kde.kdeconnect.Plugins.RunCommandPlugin; + +import android.content.Intent; +import android.os.Build; +import android.support.annotation.RequiresApi; +import android.widget.RemoteViewsService; + +@RequiresApi( api = Build.VERSION_CODES.ICE_CREAM_SANDWICH ) +public class RunCommandWidgetDataProviderService extends RemoteViewsService{ + + @Override + public RemoteViewsFactory onGetViewFactory( Intent intent ){ + + return ( new RunCommandWidgetDataProvider( RunCommandWidgetDataProviderService.this, intent ) ); + } +} \ No newline at end of file diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java new file mode 100644 --- /dev/null +++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java @@ -0,0 +1,76 @@ +package org.kde.kdeconnect.Plugins.RunCommandPlugin; + +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.RequiresApi; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.ListView; + +import org.kde.kdeconnect.BackgroundService; +import org.kde.kdeconnect.Device; +import org.kde.kdeconnect.UserInterface.List.ListAdapter; +import org.kde.kdeconnect_tp.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +@RequiresApi( api = Build.VERSION_CODES.ICE_CREAM_SANDWICH ) +public class RunCommandWidgetDeviceSelector extends AppCompatActivity{ + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + + requestWindowFeature( Window.FEATURE_NO_TITLE); + setContentView( R.layout.widget_remotecommandplugin_dialog); + + BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() { + @Override + public void onServiceStart(final BackgroundService service) { + + runOnUiThread(new Runnable() { + @Override + public void run() { + ListView view = (ListView) findViewById(R.id.runcommandsdevicelist); + + final ArrayList commandItems = new ArrayList<>(); + + for( Device device : service.getDevices().values() ){ + commandItems.add( + new CommandEntry( + device.getName(), + null, + device.getDeviceId() + ) + ); + } + + Collections.sort(commandItems, new Comparator() { + @Override + public int compare(ListAdapter.Item lhs, ListAdapter.Item rhs) { + String lName = ((CommandEntry) lhs).getName(); + String rName = ((CommandEntry) rhs).getName(); + return lName.compareTo(rName); + } + }); + + ListAdapter adapter = new ListAdapter(RunCommandWidgetDeviceSelector.this, commandItems); + + view.setAdapter(adapter); + view.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick( AdapterView adapterView, View view, int i, long l) { + CommandEntry entry = (CommandEntry) commandItems.get(i); + RunCommandWidget.setCurrentDevice( RunCommandWidgetDeviceSelector.this, entry.getKey() ); + finish(); + } + }); + } + }); + } + }); + } +}