├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml └── misc.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── trianguloy │ │ └── clipboardeditor │ │ ├── Editor.java │ │ ├── Preferences.java │ │ ├── Process.java │ │ ├── Shortcuts.java │ │ └── SimpleTextWatcher.java │ ├── play │ ├── contact-email.txt │ ├── contact-website.txt │ ├── default-language.txt │ ├── listings │ │ ├── en-US │ │ │ ├── full-description.txt │ │ │ ├── graphics │ │ │ │ ├── feature-graphic │ │ │ │ │ └── featured.png │ │ │ │ ├── icon │ │ │ │ │ └── ic_launcher-playstore.png │ │ │ │ └── phone-screenshots │ │ │ │ │ ├── 1-main.png │ │ │ │ │ ├── 2-info.png │ │ │ │ │ ├── 3-settings.png │ │ │ │ │ └── 4-other.png │ │ │ ├── short-description.txt │ │ │ └── title.txt │ │ └── fr-FR │ │ │ ├── full-description.txt │ │ │ ├── short-description.txt │ │ │ └── title.txt │ └── release-notes │ │ ├── en-US │ │ └── default.txt │ │ └── fr-FR │ │ └── default.txt │ └── res │ ├── drawable-hdpi │ └── ic_notification.png │ ├── drawable-mdpi │ └── ic_notification.png │ ├── drawable-xhdpi │ └── ic_notification.png │ ├── drawable-xxhdpi │ └── ic_notification.png │ ├── drawable │ ├── ic_launcher_foreground.xml │ ├── sync_from.xml │ └── sync_to.xml │ ├── layout │ ├── about.xml │ ├── activity_editor.xml │ └── configuration.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-es │ └── strings.xml │ ├── values-fr-rFR │ └── strings.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-v14 │ └── styles.xml │ ├── values │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── shortcuts.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | .idea/jarRepositories.xml 48 | # Android Studio 3 in .gitignore file. 49 | .idea/caches 50 | .idea/modules.xml 51 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 52 | .idea/navEditor.xml 53 | 54 | # Keystore files 55 | # Uncomment the following lines if you do not want to check your keystore files in. 56 | #*.jks 57 | #*.keystore 58 | 59 | # External native build folder generated in Android Studio 2.2 and later 60 | .externalNativeBuild 61 | .cxx/ 62 | 63 | # Google Services (e.g. APIs or Firebase) 64 | # google-services.json 65 | 66 | # Freeline 67 | freeline.py 68 | freeline/ 69 | freeline_project_description.json 70 | 71 | # fastlane 72 | fastlane/report.xml 73 | fastlane/Preview.html 74 | fastlane/screenshots 75 | fastlane/test_output 76 | fastlane/readme.md 77 | 78 | # Version control 79 | vcs.xml 80 | 81 | # lint 82 | lint/intermediates/ 83 | lint/generated/ 84 | lint/outputs/ 85 | lint/tmp/ 86 | # lint/reports/ 87 | 88 | # Android Profiling 89 | *.hprof -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Simple Clipboard Editor -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Clipboard Editor 2 | 3 | Android app made by TrianguloY 4 | 5 | Get it on Google Play:\ 6 | https://play.google.com/store/apps/details?id=com.trianguloy.clipboardeditor \ 7 | [Get it on Google Play](https://play.google.com/store/apps/details?id=com.trianguloy.clipboardeditor) 10 | 11 | Get it on F-Droid:\ 12 | https://f-droid.org/packages/com.trianguloy.clipboardeditor/ \ 13 | [Get it on F-Droid](https://f-droid.org/packages/com.trianguloy.clipboardeditor/) 16 | 17 | You can use parts of this project in your own ones, create pull request, or upload modified versions of it AS LONG AS you credit me. 18 | 19 | How to credit: 20 | - You must add my nick (TrianguloY) in an 'about' or 'acknowledgments' section visible to the user. 21 | - You must add a link to this GitHub main page (https://github.com/TrianguloY/SimpleClipboardEditor) or subpage (if you used a part of the code or an asset) in an 'about' or 'acknowledgments' section visible to the user. 22 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'com.github.triplet.play' version '3.7.0' 4 | } 5 | 6 | android { 7 | namespace 'com.trianguloy.clipboardeditor' 8 | compileSdk 32 9 | 10 | defaultConfig { 11 | applicationId "com.trianguloy.clipboardeditor" 12 | minSdk 11 13 | targetSdk 32 14 | versionCode 6 15 | versionName "1.5" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled true 21 | shrinkResources true 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_11 27 | targetCompatibility JavaVersion.VERSION_11 28 | } 29 | 30 | lint { 31 | // users are free to update translations whenever 32 | // this mean that they are not usually up to date 33 | // and that there are usually lots of missing translations 34 | // so we ignore missing strings 35 | disable 'MissingTranslation' 36 | } 37 | } 38 | 39 | dependencies { 40 | 41 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/trianguloy/clipboardeditor/Editor.java: -------------------------------------------------------------------------------- 1 | package com.trianguloy.clipboardeditor; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.app.Notification; 6 | import android.app.NotificationChannel; 7 | import android.app.NotificationManager; 8 | import android.app.PendingIntent; 9 | import android.content.ClipData; 10 | import android.content.ClipboardManager; 11 | import android.content.Context; 12 | import android.content.Intent; 13 | import android.net.Uri; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.text.Editable; 17 | import android.text.InputType; 18 | import android.util.Log; 19 | import android.view.View; 20 | import android.view.Window; 21 | import android.view.inputmethod.InputMethodManager; 22 | import android.widget.CheckBox; 23 | import android.widget.EditText; 24 | import android.widget.TextView; 25 | import android.widget.Toast; 26 | 27 | /** 28 | * The main activity, a clipboard editor 29 | */ 30 | public class Editor extends Activity { 31 | private static final String CHANNEL_ID = "text"; // id for the channel for notifications 32 | 33 | // ------------------- data ------------------- 34 | 35 | // classes 36 | private ClipboardManager clipboard; // system clipboard 37 | private NotificationManager notification; // system notifications 38 | private Preferences prefs; // preferences wrapper 39 | 40 | // views 41 | private EditText v_content; // content input 42 | private EditText v_label; // label input 43 | private TextView v_extra; // extra text 44 | 45 | // internal data 46 | private boolean noListener = false; // to avoid firing clipboardToInput and inputToClipboard recursively 47 | 48 | private final SimpleTextWatcher watcher = new SimpleTextWatcher() { 49 | @Override 50 | public void afterTextChanged(Editable s) { 51 | inputToClipboard(); 52 | } 53 | }; 54 | 55 | // ------------------- init ------------------- 56 | 57 | @Override 58 | protected void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | 61 | // activity content 62 | this.requestWindowFeature(Window.FEATURE_NO_TITLE); 63 | setContentView(R.layout.activity_editor); 64 | 65 | // views 66 | v_content = findViewById(R.id.content); 67 | v_label = findViewById(R.id.label); 68 | v_extra = findViewById(R.id.description); 69 | 70 | // descriptions 71 | for (var viewId : new int[]{R.id.notify, R.id.share, R.id.clear, R.id.configure, R.id.info, R.id.sync_to, R.id.sync_from}) { 72 | findViewById(viewId).setOnLongClickListener(view -> { 73 | Toast.makeText(Editor.this, view.getContentDescription().toString(), Toast.LENGTH_SHORT).show(); 74 | return true; 75 | }); 76 | } 77 | 78 | // preferences 79 | prefs = new Preferences(getPreferences(MODE_PRIVATE)); 80 | 81 | // clipboard 82 | clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 83 | notification = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 84 | 85 | // sync if enabled in settings 86 | updateSyncState(); 87 | 88 | // auto-update result 89 | v_content.addTextChangedListener(new SimpleTextWatcher() { 90 | @Override 91 | public void afterTextChanged(Editable s) { 92 | var intent = new Intent(); 93 | intent.putExtra(getPackageName(), inputAsPrimaryClip()); 94 | setResult(RESULT_OK, intent); 95 | } 96 | }); 97 | 98 | // show keyboard if enabled in settings 99 | if (prefs.isShowKeyboard()) { 100 | if (v_content.requestFocus()) { 101 | var imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 102 | imm.showSoftInput(v_content, InputMethodManager.SHOW_IMPLICIT); 103 | } 104 | } 105 | 106 | // capitalize input state if enabled in settings 107 | updateCapitalizeState(); 108 | 109 | // start intent 110 | parseIntent(getIntent()); 111 | setIntent(null); 112 | } 113 | 114 | @Override 115 | public void onWindowFocusChanged(boolean hasFocus) { 116 | super.onWindowFocusChanged(hasFocus); 117 | // when windows is focused, update clipboard 118 | if (hasFocus && prefs.isSync()) { 119 | clipboardToInput(); 120 | } 121 | } 122 | 123 | @Override 124 | protected void onNewIntent(Intent intent) { 125 | super.onNewIntent(intent); 126 | // on new intent (probably from a notification or from a sent text) load it 127 | parseIntent(intent); 128 | } 129 | 130 | /** 131 | * @param intent intent to parse for clipboard data 132 | */ 133 | private void parseIntent(Intent intent) { 134 | if (intent == null) return; 135 | ClipData data = null; 136 | 137 | // set by ourselves 138 | if (intent.hasExtra(getPackageName())) 139 | data = intent.getParcelableExtra(getPackageName()); 140 | // sent 141 | if (data == null && intent.hasExtra(Intent.EXTRA_TEXT)) 142 | data = ClipData.newPlainText(getString(R.string.clip_sent), intent.getStringExtra(Intent.EXTRA_TEXT)); 143 | 144 | // set 145 | if (data != null) { 146 | if (prefs.isSync()) { 147 | // set directly, it will autosync 148 | clipboard.setPrimaryClip(data); 149 | } else { 150 | // set manually 151 | clipToInput(data); 152 | } 153 | } 154 | } 155 | 156 | /** 157 | * Update 158 | */ 159 | private void updateCapitalizeState() { 160 | if (prefs.isCapitalize()) { 161 | // set flag 162 | v_content.setInputType(v_content.getInputType() | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 163 | v_label.setInputType(v_label.getInputType() | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 164 | } else { 165 | // remove flag 166 | v_content.setInputType(v_content.getInputType() & ~InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 167 | v_label.setInputType(v_label.getInputType() & ~InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); 168 | } 169 | } 170 | 171 | /** 172 | * Updates the sync state 173 | */ 174 | private void updateSyncState() { 175 | if (prefs.isSync()) { 176 | // enable clipboard to input 177 | clipboard.addPrimaryClipChangedListener(this::clipboardToInput); 178 | 179 | // enable input to clipboard 180 | v_content.addTextChangedListener(watcher); 181 | v_label.addTextChangedListener(watcher); 182 | 183 | // hide buttons 184 | findViewById(R.id.sync_parent).setVisibility(View.GONE); 185 | } else { 186 | // disable clipboard to input 187 | clipboard.removePrimaryClipChangedListener(this::clipboardToInput); 188 | 189 | // disable input to clipboard 190 | v_content.removeTextChangedListener(watcher); 191 | v_label.removeTextChangedListener(watcher); 192 | 193 | // show buttons 194 | findViewById(R.id.sync_parent).setVisibility(View.VISIBLE); 195 | } 196 | 197 | } 198 | 199 | // ------------------- buttons ------------------- 200 | 201 | /** 202 | * Shows a notification with the clipboard content 203 | */ 204 | public void onNotification(View view) { 205 | Notification.Builder builder; 206 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 207 | // setup a notification channel in Oreo+ 208 | var channel = new NotificationChannel(CHANNEL_ID, getString(R.string.channel_name), NotificationManager.IMPORTANCE_DEFAULT); 209 | channel.setDescription(getString(R.string.channel_description)); 210 | notification.createNotificationChannel(channel); 211 | 212 | builder = new Notification.Builder(this, CHANNEL_ID); 213 | } else { 214 | // no notification channel before Oreo 215 | builder = new Notification.Builder(this); 216 | } 217 | 218 | // sets the label as notification title (if any), the content and icon 219 | if (v_label.getText().length() > 0) builder.setContentTitle(v_label.getText()); 220 | builder.setContentText(v_content.getText()); 221 | builder.setSmallIcon(R.drawable.ic_notification); 222 | 223 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 224 | // when allowed, set the content as big text (improved display) 225 | builder.setStyle(new Notification.BigTextStyle() 226 | .bigText(v_content.getText())); 227 | } 228 | 229 | // sets the intent for when you click the notification. It will open the app with the current clipboard content 230 | var intent = new Intent(this, Editor.class); 231 | intent.putExtra(getPackageName(), inputAsPrimaryClip()); 232 | builder.setContentIntent(PendingIntent.getActivity(this, getUniqueId(), intent, PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0))); // change the requestCode with an unique id for multiple independent pendingIntents 233 | 234 | 235 | // publish the notification 236 | var notification = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 237 | var id = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? notification.getActiveNotifications().length : getUniqueId(); // ensure unique id 238 | notification.notify(id, 239 | Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification() 240 | ); 241 | } 242 | 243 | /** 244 | * Share the clipboard content 245 | */ 246 | public void onShare(View view) { 247 | // create a SEND text intent with the clipboard content 248 | var sendIntent = new Intent(); 249 | sendIntent.setAction(Intent.ACTION_SEND); 250 | sendIntent.putExtra(Intent.EXTRA_TEXT, v_content.getText().toString()); 251 | sendIntent.setType("text/plain"); 252 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 253 | // not sure what it does, but maybe it allows to share images (even if the app can't display them) 254 | sendIntent.setClipData(inputAsPrimaryClip()); 255 | } 256 | 257 | // start a chooser, use the label as title 258 | startActivity(Intent.createChooser(sendIntent, v_label.getText())); 259 | } 260 | 261 | /** 262 | * Clears the clipboard content 263 | */ 264 | public void onClear(View view) { 265 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 266 | if (prefs.isSync()) { 267 | // the easy way, just call 'clear' 268 | clipboard.clearPrimaryClip(); 269 | } else { 270 | // clear input only 271 | v_content.setText(""); 272 | v_label.setText(""); 273 | } 274 | } else { 275 | // the not-so-easy way, manually set as empty 276 | noListener = true; 277 | v_content.setText(""); 278 | v_label.setText(""); 279 | noListener = false; 280 | if (prefs.isSync()) { 281 | // and sync 282 | inputToClipboard(); 283 | } 284 | } 285 | } 286 | 287 | /** 288 | * Open the configure screen 289 | */ 290 | public void onConfigure(View view) { 291 | // setup 292 | var content = getLayoutInflater().inflate(R.layout.configuration, null); 293 | 294 | // auto keyboard 295 | CheckBox autokeyboard = content.findViewById(R.id.autokeyboard); 296 | autokeyboard.setChecked(prefs.isShowKeyboard()); 297 | autokeyboard.setOnCheckedChangeListener((checkbox, checked) -> prefs.setShowKeyboard(checked)); 298 | 299 | // capitalize 300 | CheckBox capitalize = content.findViewById(R.id.capitalize); 301 | capitalize.setChecked(prefs.isCapitalize()); 302 | capitalize.setOnCheckedChangeListener((checkbox, checked) -> { 303 | prefs.setCapitalize(checked); 304 | updateCapitalizeState(); 305 | }); 306 | 307 | // autosync 308 | CheckBox sync = content.findViewById(R.id.sync); 309 | sync.setChecked(prefs.isSync()); 310 | sync.setOnCheckedChangeListener((checkbox, checked) -> { 311 | prefs.setSync(checked); 312 | updateSyncState(); 313 | }); 314 | 315 | // show 316 | new AlertDialog.Builder(this) 317 | .setIcon(R.mipmap.ic_launcher) 318 | .setTitle(R.string.descr_configure) 319 | .setView(content) 320 | .show(); 321 | } 322 | 323 | /** 324 | * Show the about screen 325 | */ 326 | public void onInfo(View view) { 327 | // before-setup 328 | var content = getLayoutInflater().inflate(R.layout.about, null); 329 | 330 | // show 331 | var dialog = new AlertDialog.Builder(this) 332 | .setIcon(R.mipmap.ic_launcher) 333 | .setTitle(getString(R.string.descr_info)) 334 | .setView(content) 335 | .show(); 336 | 337 | // after-setup 338 | for (var id : new int[]{R.id.blog, R.id.github, R.id.playstore}) { 339 | // setup all three buttons (its tag is the url) 340 | var button = content.findViewById(id); 341 | button.setOnClickListener(btn -> { 342 | // click to open in browser 343 | var url = btn.getTag().toString(); 344 | try { 345 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); 346 | } catch (Exception e) { 347 | // on error, like no browser, invoke long click (set the url in the clipboard) 348 | btn.performLongClick(); 349 | } 350 | dialog.dismiss(); 351 | }); 352 | button.setOnLongClickListener(btn -> { 353 | // long click to set in clipboard 354 | clipboard.setPrimaryClip(ClipData.newPlainText("TrianguloY", btn.getTag().toString())); 355 | dialog.dismiss(); 356 | return true; 357 | }); 358 | } 359 | } 360 | 361 | /** 362 | * @see this#clipboardToInput 363 | */ 364 | public void inputFromClipboard(View view) { 365 | clipboardToInput(); 366 | } 367 | 368 | /** 369 | * @see this#inputToClipboard() 370 | */ 371 | public void inputToClipboard(View view) { 372 | inputToClipboard(); 373 | } 374 | 375 | 376 | // ------------------- transfer ------------------- 377 | 378 | /** 379 | * Sets the input values to the clipboard ones 380 | * Ensures it doesn't fire inputToClipboard 381 | */ 382 | private void clipboardToInput() { 383 | if (noListener) return; 384 | noListener = true; 385 | 386 | // get 387 | clipToInput(clipboard.getPrimaryClip()); 388 | } 389 | 390 | /** 391 | * Sets the inputs to the values of the clipdata 392 | */ 393 | private void clipToInput(ClipData clip) { 394 | // set 395 | if (clip == null) { 396 | // no content 397 | v_extra.setText(String.format("[%s]", getString(R.string.txt_empty))); 398 | v_label.setText(""); 399 | v_content.setText(""); 400 | 401 | Log.d("CLIPBOARD", "--> null"); 402 | } else { 403 | // content 404 | var description = clip.getDescription(); 405 | 406 | // mimetype 407 | v_extra.setText(R.string.label_mimetype); 408 | var empty = true; 409 | for (var i = 0; i < description.getMimeTypeCount(); i++) { 410 | if (!empty) v_extra.append(" -"); 411 | empty = false; 412 | v_extra.append(" " + description.getMimeType(i)); 413 | } 414 | if (empty) v_extra.append(getString(R.string.txt_empty)); 415 | 416 | // item count 417 | var itemCount = clip.getItemCount(); 418 | if (itemCount > 1) v_extra.append(getString(R.string.txt_itemcount) + itemCount); 419 | 420 | // label 421 | var label = toStringNonNull(description.getLabel()); 422 | if (!toStringNonNull(v_label.getText()).equals(label)) { 423 | v_label.setText(label); 424 | if (v_label.hasFocus()) v_label.setSelection(v_label.getText().length()); 425 | } 426 | 427 | // text 428 | var content = toStringNonNull(clip.getItemAt(0).coerceToText(this)); 429 | if (!toStringNonNull(v_content.getText()).equals(content)) { 430 | v_content.setText(content); 431 | if (v_content.hasFocus()) v_content.setSelection(v_content.getText().length()); 432 | } 433 | 434 | 435 | Log.d("CLIPBOARD", "--> [" + label + "] " + content); 436 | } 437 | 438 | 439 | noListener = false; 440 | } 441 | 442 | /** 443 | * Sets the clipboard value to the input ones 444 | * Ensures it doesn't fire clipboardToInput 445 | */ 446 | private void inputToClipboard() { 447 | if (noListener) return; 448 | noListener = true; 449 | 450 | // get 451 | CharSequence content = v_content.getText(); 452 | CharSequence label = v_label.getText(); 453 | 454 | // set 455 | clipboard.setPrimaryClip(ClipData.newPlainText(label, content)); 456 | 457 | Log.d("CLIPBOARD", "<-- [" + label + "] " + content); 458 | 459 | noListener = false; 460 | } 461 | 462 | /** 463 | * Returns the input as primary clip 464 | * in sync mode, the clipboard one is returned (may contain more data) 465 | */ 466 | private ClipData inputAsPrimaryClip() { 467 | return prefs.isSync() 468 | ? clipboard.getPrimaryClip() 469 | : ClipData.newPlainText(v_label.getText().toString(), v_content.getText().toString()); 470 | } 471 | 472 | // ------------------- utils ------------------- 473 | 474 | /** 475 | * return object?.toString() ?: ""; 476 | * 477 | * @param object any object, including null 478 | * @return non null string 479 | */ 480 | static private String toStringNonNull(Object object) { 481 | return object == null ? "" : object.toString(); 482 | } 483 | 484 | /** 485 | * @return a 'unique' id (should be unique unless called at the same millisecond or after a very VERY long time) 486 | */ 487 | private static int getUniqueId() { 488 | return Long.valueOf(System.currentTimeMillis()).intValue(); 489 | } 490 | 491 | } -------------------------------------------------------------------------------- /app/src/main/java/com/trianguloy/clipboardeditor/Preferences.java: -------------------------------------------------------------------------------- 1 | package com.trianguloy.clipboardeditor; 2 | 3 | import android.content.SharedPreferences; 4 | 5 | /** 6 | * Simple Preferences wrapper 7 | */ 8 | public class Preferences { 9 | private final SharedPreferences prefs; // the prefs 10 | 11 | /** 12 | * @param prefs preferences to wrap 13 | */ 14 | public Preferences(SharedPreferences prefs) { 15 | this.prefs = prefs; 16 | } 17 | 18 | // ------------------- autoshowkeyboard ------------------- 19 | private final String SHOWKEYBOARD_KEY = "showKeyboard"; 20 | private final boolean SHOWKEYBOARD_DEFAULT = true; 21 | 22 | /** 23 | * @return if SHOWKEYBOARD preference is set 24 | */ 25 | public boolean isShowKeyboard() { 26 | return prefs.getBoolean(SHOWKEYBOARD_KEY, SHOWKEYBOARD_DEFAULT); 27 | } 28 | 29 | 30 | /** 31 | * @param showKeyboard new SHOWKEYBOARD preference to set 32 | */ 33 | public void setShowKeyboard(boolean showKeyboard) { 34 | prefs.edit().putBoolean(SHOWKEYBOARD_KEY, showKeyboard).apply(); 35 | } 36 | 37 | // ------------------- capitalize sentences ------------------- 38 | 39 | private final String CAPITALIZE_KEY = "capitalize"; 40 | private final boolean CAPITALIZE_DEFAULT = false; 41 | 42 | /** 43 | * @return if CAPITALIZE preference is set 44 | */ 45 | public boolean isCapitalize() { 46 | return prefs.getBoolean(CAPITALIZE_KEY, CAPITALIZE_DEFAULT); 47 | } 48 | 49 | 50 | /** 51 | * @param capitalize new CAPITALIZE preference to set 52 | */ 53 | public void setCapitalize(boolean capitalize) { 54 | prefs.edit().putBoolean(CAPITALIZE_KEY, capitalize).apply(); 55 | } 56 | 57 | // ------------------- auto sync ------------------- 58 | 59 | private final String SYNC_KEY = "sync"; 60 | private final boolean SYNC_DEFAULT = true; 61 | 62 | /** 63 | * @return if SYNC preference is set 64 | */ 65 | public boolean isSync() { 66 | return prefs.getBoolean(SYNC_KEY, SYNC_DEFAULT); 67 | } 68 | 69 | 70 | /** 71 | * @param sync new SYNC preference to set 72 | */ 73 | public void setSync(boolean sync) { 74 | prefs.edit().putBoolean(SYNC_KEY, sync).apply(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/trianguloy/clipboardeditor/Process.java: -------------------------------------------------------------------------------- 1 | package com.trianguloy.clipboardeditor; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.ClipData; 6 | import android.content.Intent; 7 | import android.os.Build; 8 | import android.os.Bundle; 9 | 10 | /** 11 | * This activity receives the PROCESS_TEXT intent and calls the main activity with it. 12 | * Separated to allow having a different label, and also because this feature is for Android 6.0+ only 13 | */ 14 | @TargetApi(Build.VERSION_CODES.M) 15 | public class Process extends Activity { 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | 21 | // get the text 22 | var clipData = ClipData.newPlainText( 23 | getString(R.string.clip_selection), 24 | getIntent().getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT) 25 | ); 26 | 27 | // process the text 28 | var intent = new Intent(this, Editor.class); 29 | intent.putExtra(getPackageName(), clipData); 30 | startActivityForResult(intent, 0); 31 | } 32 | 33 | @Override 34 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 35 | super.onActivityResult(requestCode, resultCode, data); 36 | 37 | // get the result 38 | var clipData = data == null ? null : data.getParcelableExtra(getPackageName()); 39 | var result = clipData == null ? "" : clipData.getItemAt(0).coerceToText(this); 40 | 41 | // return it (we ignore the readonly attribute, it's unnecessary) 42 | var intent = new Intent(); 43 | intent.putExtra(Intent.EXTRA_PROCESS_TEXT, result); 44 | setResult(RESULT_OK, intent); 45 | finish(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/trianguloy/clipboardeditor/Shortcuts.java: -------------------------------------------------------------------------------- 1 | package com.trianguloy.clipboardeditor; 2 | 3 | import android.app.Activity; 4 | import android.content.ClipData; 5 | import android.content.ClipboardManager; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.widget.Toast; 9 | 10 | /** 11 | * Activity that will clear the clipboard when launched, then exit 12 | */ 13 | public class Shortcuts extends Activity { 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | 18 | // get 19 | var clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 20 | 21 | // clear 22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 23 | // the easy way, just call 'clear' 24 | clipboard.clearPrimaryClip(); 25 | } else { 26 | // the not-so-easy way, manually set as empty 27 | clipboard.setPrimaryClip(ClipData.newPlainText("", "")); 28 | } 29 | 30 | Toast.makeText(this, R.string.toast_cleared, Toast.LENGTH_SHORT).show(); 31 | finish(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/trianguloy/clipboardeditor/SimpleTextWatcher.java: -------------------------------------------------------------------------------- 1 | package com.trianguloy.clipboardeditor; 2 | 3 | import android.text.Editable; 4 | import android.text.TextWatcher; 5 | 6 | /** 7 | * {@link TextWatcher} is an interface, with 3 methods, and you usually don't need all of them. This class overrides them all so you don't need to 8 | */ 9 | public class SimpleTextWatcher implements TextWatcher { 10 | @Override 11 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 12 | } 13 | 14 | @Override 15 | public void onTextChanged(CharSequence s, int start, int before, int count) { 16 | } 17 | 18 | @Override 19 | public void afterTextChanged(Editable s) { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/play/contact-email.txt: -------------------------------------------------------------------------------- 1 | correo--correo+sce@hotmail.com 2 | -------------------------------------------------------------------------------- /app/src/main/play/contact-website.txt: -------------------------------------------------------------------------------- 1 | https://triangularapps.blogspot.com/ 2 | -------------------------------------------------------------------------------- /app/src/main/play/default-language.txt: -------------------------------------------------------------------------------- 1 | en-US 2 | -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/full-description.txt: -------------------------------------------------------------------------------- 1 | This app displays the current content of your phone's clipboard. 2 | When that content is text, you can edit, replace, delete or perform any other text modification, live. 3 | Additionally, you can also share the content directly and create a notification with it for later use. 4 | 5 | Note: Due to Google's restrictions the app can't interact with the clipboard while it's closed. It will be automatically updated as soon as you reopen it though. The app doesn't contains any history functionality (not yet at least). 6 | 7 | 8 | Made by TrianguloY. Extremely small, efficient, without any permissions, free and without ads. 9 | 10 | GitHub: https://github.com/TrianguloY/SimpleClipboardEditor 11 | Blog: https://triangularapps.blogspot.com/ 12 | -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/feature-graphic/featured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/feature-graphic/featured.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/icon/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/icon/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/phone-screenshots/1-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/phone-screenshots/1-main.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/phone-screenshots/2-info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/phone-screenshots/2-info.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/phone-screenshots/3-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/phone-screenshots/3-settings.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/graphics/phone-screenshots/4-other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/play/listings/en-US/graphics/phone-screenshots/4-other.png -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/short-description.txt: -------------------------------------------------------------------------------- 1 | View, edit or delete the phone clipboard's text with a simple and fast editor. 2 | -------------------------------------------------------------------------------- /app/src/main/play/listings/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Simple Clipboard Editor 2 | -------------------------------------------------------------------------------- /app/src/main/play/listings/fr-FR/full-description.txt: -------------------------------------------------------------------------------- 1 | Cette application affiche le contenu actuel du presse-papiers de votre téléphone. 2 | Lorsque ce contenu est du texte, vous pouvez le modifier, remplacer, supprimer ou effectuer toute autre modification, en direct. 3 | De plus, vous pouvez également partager se contenu directement et créer une notification avec lui pour une utilisation ultérieure. 4 | 5 | Remarque: En raison des restrictions de Google, l'application ne peut pas interagir avec le presse-papiers lorsqu'il est fermé. Cependant, il sera automatiquement mis à jour dès que vous le rouvrirez. L'application ne contient aucune fonctionnalité d'historique (du moins pas encore). 6 | 7 | 8 | Réalisé par TrianguloY. Léger, efficace, sans aucune autorisation, gratuit et sans publicité. 9 | -------------------------------------------------------------------------------- /app/src/main/play/listings/fr-FR/short-description.txt: -------------------------------------------------------------------------------- 1 | intéragissez avec le presse-papiers de votre smartphone via un éditeur efficace. 2 | -------------------------------------------------------------------------------- /app/src/main/play/listings/fr-FR/title.txt: -------------------------------------------------------------------------------- 1 | Simple Clipboard Editor 2 | -------------------------------------------------------------------------------- /app/src/main/play/release-notes/en-US/default.txt: -------------------------------------------------------------------------------- 1 | V 1.5 2 | - Sent text to the app 3 | - Edit selected text (Android 6.0+) 4 | 5 | V 1.4 6 | - New shortcut to clear the clipboard directly without opening the app 7 | - Setting to disable automatic sync 8 | - New translation to Portuguese (Brazil) thanks to mezysinc! 9 | - New translation to Spanish 10 | - Fix launcher icon not shown in older android versions 11 | 12 | V 1.3 13 | - New setting: Capitalize sentences on input 14 | - Tweaks to texts and visuals of dialogs 15 | 16 | V 1.2 17 | - Style update 18 | 19 | V 1.1 20 | - Added Github and Blog links to about screen 21 | - Fixed sharing 22 | 23 | V 1.0 24 | - Initial release on Play Store -------------------------------------------------------------------------------- /app/src/main/play/release-notes/fr-FR/default.txt: -------------------------------------------------------------------------------- 1 | V1.5 2 | - Envoyé du texte à l'application 3 | - Éditer le texte sélectionné (Android 6.0+) 4 | 5 | V1.4 6 | - Nouveau raccourci pour effacer le presse-papiers directement sans ouvrir l'application 7 | - Nouveau paramètre: désactiver la synchronisation automatique 8 | - Nouvelle traduction en portugais (Brésil) grâce à mezysinc ! 9 | - Nouvelle traduction en espagnol 10 | - Correction de l'icône non affichée dans les anciennes versions d'Android 11 | 12 | v1.3 13 | - Nouveau paramètre: capitaliser les phrases lors de la saisie 14 | - Ajustements des textes et des visuels des boîtes de dialogue 15 | 16 | V1.2 17 | - Mise à jour des styles 18 | 19 | V1.1 20 | - Ajout de liens vers Github et Blog dans l'écran "À Propos" 21 | - Partage fixe 22 | 23 | V1.0 24 | - Première version sur Play Store 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/res/drawable-hdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/res/drawable-mdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/res/drawable-xhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrianguloY/SimpleClipboardEditor/fe38d45d492348c3c4b1602967f4a046a4a6be60/app/src/main/res/drawable-xxhdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sync_from.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sync_to.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 |