Initial commit

This commit is contained in:
William Brawner 2017-07-24 23:37:23 -05:00
commit 31bed24bd8
32 changed files with 701 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

1
app/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

35
app/build.gradle Normal file
View file

@ -0,0 +1,35 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.0"
defaultConfig {
applicationId "com.wbrawner.simplemarkdown"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:26.+'
compile 'us.feras.mdv:markdownview:1.1.0'
compile 'com.jakewharton:butterknife:8.7.0'
compile 'com.android.support:support-v4:26.+'
testCompile 'junit:junit:4.12'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
}

25
app/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/billy/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,26 @@
package com.wbrawner.simplemarkdown;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.wbrawner.simplemarkdown", appContext.getPackageName());
}
}

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wbrawner.simplemarkdown">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"></action>
<category android:name="android.intent.category.LAUNCHER"></category>
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.wbrawner.simplemarkdown.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View file

@ -0,0 +1,171 @@
package com.wbrawner.simplemarkdown;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import butterknife.BindView;
import butterknife.ButterKnife;
import static android.content.ContentValues.TAG;
public class EditFragment extends Fragment {
public static final String SAVE_ACTION = "com.wbrawner.simplemarkdown.ACTION_SAVE";
@BindView(R.id.markdown_edit) EditText markdownEditor;
private File mTmpFile;
public EditFragment() {
// Required empty public constructor
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction(SAVE_ACTION);
LocalBroadcastManager.getInstance(getContext()).registerReceiver(
new EditFragment.MarkdownBroadcastSaveReceiver(),
filter
);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_edit, container, false);
ButterKnife.bind(this, view);
if (markdownEditor.requestFocus()) {
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
};
markdownEditor.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
updatePreview(markdownEditor.getText());
}
@Override
public void afterTextChanged(Editable editable) {
}
});
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "Markdown from beginning: " + markdownEditor.getText().toString());
BufferedReader reader = null;
try {
File tmpFile = new File(getActivity().getFilesDir() + MainActivity.getTempFileName());
if (tmpFile.exists()) {
Log.d(TAG, "Temp file size: " + tmpFile.length());
InputStream in = new FileInputStream(tmpFile);
reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
markdownEditor.append(line);
markdownEditor.append("\r\n");
}
}
} catch (Exception e) {
Log.e(TAG, "Error reading temp file: ", e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
}
}
}
if (markdownEditor.getText().length() > 0) {
Log.d(TAG, "We have text");
} else {
Log.d(TAG, "Blank slate");
}
updatePreview(markdownEditor.getText());
}
private void updatePreview(Editable data) {
Log.d(TAG, "Data to send: " + data.toString());
Intent broadcastIntent = new Intent(PreviewFragment.PREVIEW_ACTION);
broadcastIntent.putExtra("markdownData", data.toString());
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getContext());
manager.sendBroadcast(broadcastIntent);
}
public void save(String data, String filePath) {
if (filePath == null)
filePath = MainActivity.getFilePath() + MainActivity.getFileName();
FileOutputStream out = null;
try {
File tmpFile = new File(filePath);
out = new FileOutputStream(tmpFile);
out.write(data.getBytes());
} catch (Exception e) {
Log.e(TAG, "Error saving temp file:", e);
} finally {
try {
if (out != null)
out.close();
} catch (IOException e) {
Log.e(TAG, "Error closing write stream", e);
}
}
}
public void save(String data) {
save(data, null);
}
public void save(Editable data) {
save(data.toString(), null);
}
@Override
public void onPause() {
save(markdownEditor.getText().toString(),
MainActivity.getTempFilePath() + MainActivity.getFileName());
super.onPause();
}
private class MarkdownBroadcastSaveReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra("fileName")) {
String fileName = intent.getStringExtra("fileName");
Log.d(TAG, "File: " + fileName);
save(markdownEditor.getText().toString(), fileName);
}
}
}
}

View file

@ -0,0 +1,163 @@
package com.wbrawner.simplemarkdown;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.FileProvider;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import java.io.File;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
private static final String AUTHORITY = "com.wbrawner.simplemarkdown.fileprovider";
private static File mFilesDir;
@BindView(R.id.pager) ViewPager pager;
@BindView(R.id.layout_tab) TabLayout tabLayout;
private static final String TAG = MainActivity.class.getSimpleName();
private static String fileName;
public static String getTempFileName() {
return "com_wbrawner_simplemarkdown_tmp.md";
}
public static String getFileName() {
if (fileName == null) {
return getTempFileName();
}
return fileName;
}
public static String getTempFilePath() {
return mFilesDir + File.pathSeparator + "tmp" + File.pathSeparator;
}
public static String getFilePath() {
return mFilesDir + File.pathSeparator + "saved_files" + File.pathSeparator;
}
public static void setFileName(String fileName) {
MainActivity.fileName = fileName;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
pager.setAdapter(new EditPagerAdapter(getSupportFragmentManager()));
mFilesDir = getFilesDir();
}
// @Override
// public boolean onCreateOptionsMenu(Menu menu) {
// getMenuInflater().inflate(R.menu.menu_edit, menu);
// return true;
// }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_save:
//TODO: Create popup for file name
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.action_save);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setHint(R.string.hint_filename);
builder.setView(input);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent saveIntent = new Intent(EditFragment.SAVE_ACTION);
saveIntent.putExtra("fileName", input.getText());
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(saveIntent);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
break;
case R.id.action_share:
//TODO: Fix this
File tmpFile = new File(getFilesDir() + getTempFileName());
if (tmpFile.exists()) {
Log.d(TAG, "Temp file size: " + tmpFile.length());
Uri fileUri = FileProvider.getUriForFile(MainActivity.this, AUTHORITY, tmpFile);
if (fileUri != null) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(shareIntent);
}
}
break;
}
return super.onOptionsItemSelected(item);
}
public class EditPagerAdapter extends FragmentPagerAdapter {
private static final int FRAGMENT_EDIT = 0;
private static final int FRAGMENT_PREVIEW = 1;
private static final int NUM_PAGES = 2;
public EditPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case FRAGMENT_EDIT:
return new EditFragment();
case FRAGMENT_PREVIEW:
return new PreviewFragment();
}
return null;
}
@Override
public int getCount() {
return NUM_PAGES;
}
@Override
public CharSequence getPageTitle(int position) {
int stringId = 0;
switch (position) {
case FRAGMENT_EDIT:
stringId = R.string.action_edit;
break;
case FRAGMENT_PREVIEW:
stringId = R.string.action_preview;
break;
}
return getString(stringId);
}
}
}

View file

@ -0,0 +1,63 @@
package com.wbrawner.simplemarkdown;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import butterknife.BindView;
import butterknife.ButterKnife;
import us.feras.mdv.MarkdownView;
public class PreviewFragment extends Fragment {
private static final String TAG = PreviewFragment.class.getSimpleName();
@BindView(R.id.markdown_view)
MarkdownView markdownView;
public static final String PREVIEW_ACTION = "com.wbrawner.simplemarkdown.preview";
public PreviewFragment() {
// Required empty public constructor
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction(PREVIEW_ACTION);
LocalBroadcastManager.getInstance(getContext()).registerReceiver(
new MarkdownBroadcastSender(),
filter
);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_preview, container, false);
ButterKnife.bind(this, view);
return view;
}
private class MarkdownBroadcastSender extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.hasExtra("markdownData")) {
String data = intent.getStringExtra("markdownData");
Log.d(TAG, "Markdown Data: " + data);
markdownView.loadMarkdown(data);
}
}
}
}

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/layout_tab"
android:layout_gravity="top">
<android.support.design.widget.TabItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_edit"
android:id="@+id/tab_edit" />
<android.support.design.widget.TabItem
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/action_preview"
android:id="@+id/tab_preview" />
</android.support.design.widget.TabLayout>
</android.support.v4.view.ViewPager>
</LinearLayout>

View file

@ -0,0 +1,16 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
tools:context="com.wbrawner.simplemarkdown.EditFragment">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/markdown_edit"
android:inputType="textMultiLine"
android:background="@null"
android:hint="@string/markdown_here"
android:scrollHorizontally="false" />
</ScrollView>

View file

@ -0,0 +1,12 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.wbrawner.simplemarkdown.PreviewFragment">
<us.feras.mdv.MarkdownView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/markdown_view" />
</FrameLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:title="@string/action_save"
android:id="@+id/action_save"
android:icon="@android:drawable/ic_menu_save"
app:showAsAction="always" />
<item
android:title="@string/action_share"
android:id="@+id/action_share"
android:icon="@android:drawable/ic_menu_share"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.wbrawner.simplemarkdown.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
<item
android:id="@+id/action_help"
android:orderInCategory="100"
android:title="@string/action_help"
app:showAsAction="never" />
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>

View file

@ -0,0 +1,14 @@
<resources>
<string name="app_name">Simple Markdown</string>
<string name="action_settings">Settings</string>
<string name="action_help">Help</string>
<string name="action_edit">Edit</string>
<string name="action_preview">Preview</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="markdown_here">Markdown here...</string>
<string name="action_save">Save</string>
<string name="action_share">Share</string>
<string name="hint_filename">File name</string>
</resources>

View file

@ -0,0 +1,20 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="markdown_files" path="/"/>
</paths>

View file

@ -0,0 +1,17 @@
package com.wbrawner.simplemarkdown;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

23
build.gradle Normal file
View file

@ -0,0 +1,23 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}