ContentProvider 기능이 필요해서 가져다 사용 하려는데,

현재 검색되는 코드들은 대부분 되는 것들이 없어서 (코드 자체로는 전혀 문제가 없었지만 추가사항이 있어서..) 코드 공유 합니다.


기본적인 설명으로는 여기를 

https://developer.android.com/guide/topics/providers/content-provider-creating.html?hl=ko


이외 코드 관련 부분은 아래에서 참고하였습니다.

참고URL : https://www.tutorialspoint.com/android/android_content_providers.htm



주의사항!

studio가 불안정한건지, 아님 저의 툴 설정이 문제인건지, 여러번 빌드시 이전 provider 설정들이 남아 있는건지

정상 동작하지 않던 문제가 있었습니다. 

저같은 경우는 항상 A,B 앱을 uninstall 후에 빌드 시 제대로된 결과을 얻을 수 있었습니다.

(Permission Denial: opening provider com.app.first.myContentProvider from ..... 등등이 나온다면...)



1. A앱 작업 Mainfests (퍼미션을 추가합니다. 프로바이더 내에서도 readPermission과 writePermission , exprted를 명시합니다.)

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.app.first">     <!-- 퍼미션 추가 --> <permission android:name="com.app.first.READ_DATABASE" android:protectionLevel="normal" /> <permission android:name="com.app.first.WRITE_DATABASE" android:protectionLevel="normal" /> <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" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 프로바이더 등록 --> <provider android:name=".myContentProvider" android:authorities="com.app.first.myContentProvider" android:exported="true" android:readPermission="com.app.first.READ_DATABASE" android:writePermission="com.app.first.WRITE_DATABASE"/> </application> </manifest>

activity_main.xml (레이아웃 만들기)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="16dp"
    tools:context="com.app.first.MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="데이터 불러오기"
        android:id="@+id/bt_renew"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_below="@id/bt_renew"
        android:textSize="15sp"
        android:text="Hello World!"
        android:id="@+id/tv_text"/>

</RelativeLayout>

MainActivity.java (메인 화면 처리)

package com.app.first;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    DBHelper mDatabase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mDatabase = new DBHelper(getBaseContext());

        // 데이터 입력
        mDatabase.setDelete();
        List<ItemRow> mList = new ArrayList<>();
        mList.add(new ItemRow("식사","미역국",5000));
        mList.add(new ItemRow("간식","우유",4000));
        mList.add(new ItemRow("간식","바나나",3000));
        mList.add(new ItemRow("식사","오이",2000));
        mList.add(new ItemRow("식사","당근",1000));
        for(ItemRow item : mList) {
            mDatabase.setItem(item.contents, item.name, item.num);
        }

        Button bt_renew = (Button) findViewById(R.id.bt_renew);
        bt_renew.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 데이터 불러오기
                loadData();
            }
        });
    }

    private void loadData() {
        StringBuilder sb = new StringBuilder();
        List<ItemRow> row = mDatabase.getItem();
        sb.append("Total count : "+row.size()+"\n\n");
        for(ItemRow item : row) {
            sb.append(item.contents+" , "+item.name+" , "+item.num+"\n");
        }
        TextView tv_text = (TextView) findViewById(R.id.tv_text);
        tv_text.setText(sb.toString());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(mDatabase != null)
            mDatabase.close();
    }
}

class ItemRow {
    public String contents;
    public String name;
    public int num;

    public ItemRow(String contents, String name, int num) {
        this.contents = contents;
        this.name = name;
        this.num = num;
    }
}

DBHelper.java (디비 로직 작업)

package com.app.first;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;

import java.util.ArrayList;
import java.util.List;

public class DBHelper extends SQLiteOpenHelper {

    private static final int db_version = 1;					// Database Version
    private static final String DB_FILE_NAME = "UserData.db";

    private static final String [] COLUMNS = {"contents TEXT", "name TEXT", "num INTEGER"};
    public static String TABLE_NAME = "my_table";

    public DBHelper(Context context) {
        // TODO  http://stackoverflow.com/questions/4547461/closing-the-database-in-a-contentprovider
        super(context, DB_FILE_NAME, null, db_version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table " + TABLE_NAME +
                " (" + BaseColumns._ID + " integer primary key autoincrement ";
        for (int i = 0; i < COLUMNS.length; i++) {
            sql += ", " + COLUMNS[i];
        }
        sql += " ) ";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

    public List<ItemRow> getItem() {
        List<ItemRow> list = new ArrayList<>();
        try {
            beginTransaction();
            Cursor c = getAll(TABLE_NAME);
            if (c != null) {
                int total = c.getCount();
                if (total > 0) {
                    c.moveToFirst();
                    while (!c.isAfterLast()) {
                        String contents = c.getString(1);
                        String name = c.getString(2);
                        int num = c.getInt(3);
                        list.add(new ItemRow(contents, name, num));
                        c.moveToNext();
                    }
                }
                c.close();
            }
        } catch (SQLiteException e) {
            e.printStackTrace();
        } finally {
            endTransaction();
        }
        return list;
    }

    public void setItem(String contents, String name, int num) throws SQLiteException{
        ContentValues values = new ContentValues();
        values.put("contents", contents);
        values.put("name", name);
        values.put("num", num);
        insert(TABLE_NAME, values);
    }

    public void setDelete() {
        AllDelete(TABLE_NAME);
    }

    protected Cursor getAll(String tableName) throws SQLiteException {
        return getReadableDatabase().query(tableName, null, null, null, null, null, /*"date desc"*/null);
    }

    protected void beginTransaction() {
        getWritableDatabase().beginTransaction();
    }
    protected void endTransaction() {
        getWritableDatabase().setTransactionSuccessful();   // db 속도 향상
        getWritableDatabase().endTransaction();
    }

    protected void insert(String tableName, ContentValues values) throws SQLiteException {
        getWritableDatabase().insert(tableName, null, values);
    }

    protected void AllDelete(String tableName) throws SQLiteException{
        getWritableDatabase().delete(tableName, null, null);
    }
}

myContentProvider.java (컨텐트프로바이더 내의 쿼리 처리)

package com.app.first; import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import java.util.HashMap; public class myContentProvider extends ContentProvider { private SQLiteDatabase mDatabase; static final String PROVIDER_NAME = "com.app.first.myContentProvider"; private static HashMap<String, String> STUDENTS_PROJECTION_MAP; static final int GET_ALL = 1; static final int INSERT = 2; static final int UPDATE = 3; static final int DELETE = 4; static final UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "getAll", GET_ALL); uriMatcher.addURI(PROVIDER_NAME, "insert", INSERT); } @Override public boolean onCreate() { DBHelper dbHelper = new DBHelper(getContext()); mDatabase = dbHelper.getWritableDatabase(); return (mDatabase == null)? false:true; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { Log.d("test","uri : "+uri); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables("my_table"); //switch (uriMatcher.match(uri)) { // case STUDENTS: // qb.setProjectionMap(STUDENTS_PROJECTION_MAP); // break; // case STUDENT_ID: // qb.appendWhere( _ID + "=" + uri.getPathSegments().get(1)); // break; // default: // } qb.setProjectionMap(STUDENTS_PROJECTION_MAP); if (sortOrder == null || sortOrder == ""){ sortOrder = /*NAME*/"contents"; } Cursor c = qb.query(mDatabase, projection, selection, selectionArgs,null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Nullable @Override public String getType(@NonNull Uri uri) { return null; } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } }


2. B앱 Mainfests (퍼미션 설정을 추가합니다.)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.app.second">

    <!-- 퍼미션 설정 -->
    <uses-permission android:name="com.app.first.READ_DATABASE" />
    <uses-permission android:name="com.app.first.WRITE_DATABASE" />
    <!-- 퍼미션 설정 -->

    <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" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.app.second.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
MainActivity.java (A앱의 myContentProvider에 쿼리를 날려 데이터를 가져옵니다.)
package com.app.second;

import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    String authority = "com.app.first.myContentProvider";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Cursor c = getContentResolver().query(Uri.parse("content://"+authority+"/getdataall"), null, null, null, null);
        if(c == null)
            return;
        Log.e("test","aaabbb "+c.getCount());

        StringBuilder aa = new StringBuilder();
        while(c.moveToNext()) {
            String str = c.getLong(0) + " , " + c.getString(1)+" , "+c.getString(2)+" , "+c.getInt(3);
            System.out.println(str);
            aa.append(str+"\n");
        }
        c.close();

        TextView text = (TextView) findViewById(R.id.text);
        text.setText(aa);
    }
}



3. 결과 화면 


A앱                                                         B앱

    

[출처] XuXu 님의 [랜섬웨어 예방] 공유기(iptime)설정에서 랜섬웨어 공격포트 차단하기

http://blog.naver.com/jstour78/221005798723



iptime 공유기에서 랜섬웨어 예방하는 방법을 공유합니다.

집에 pc가 여러대일 경우 일일이 하는 포트 설정하는 일은 너무 귀찮은 일입니다.

그래서 모든 공유기에는 자체적으로 포트 차단하는 기능들이 있어서 적용을 하도록 하겠습니다.

다른 공유기는 공식 사이트나 구글서치로 확인할 수 있겠습니다.


* 주의 : 랜섬웨어를 막기위해서 137(UDP), 138(UDP), 139(TCP), 445(TCP) 를 막아주는데,

간혹 공유 프린터나 기타 장비들이 사용하는 경우도 있으므로 확인하시고, 작업을 하셔야 합니다.



1. 공유기에 접속을 하여 > 고급 설정 탭 > 보안 기능 > 인터넷/WiFi 사용 제한을 선택합니다.




2. 새 규칙을 눌러 이름을 입력 후 내부 <-> 외부 버튼을 선택합니다.



 

3. UDP(137,138 - TCP는 139, 445)를 선택 후 




4. (137 ~ 137) 포트 입력 , 모든 IP 주소 선택 후 적용




5. 완료 화면 ( 추가로 138 , 139 , 445 까지 해주면 됩니다.)

137 포트 UDP

138 포트 UDP

139 포트 TCP

445 포트 TCP


'etc' 카테고리의 다른 글

GitLab 사용하기 - 2. SSH 키 등록하기  (0) 2017.11.08
GitLab 사용하기 - 1. SSH 키 생성하기  (0) 2017.11.08
개인정보취급방침  (0) 2017.05.03
소스 코드 테스트  (0) 2017.04.06
새프로젝트 로고 시안  (0) 2017.04.05


기본적으로 SearchView 를 사용 할 경우에는 (그림과 같은) Done 버튼이 정삭적으로 사용이 가능하지만

이 ActionBar의 경우는 여러가지 처리를 위해 커스텀으로 만들어졌습니다.

그래서 Done 버튼의 이벤트를 받기 위해서는 별도의 작업을 해줘야 사용이 가능합니다.



EditText (최신 대응은 AppCompatEditText) 를 상속받아 별도의 클래스를 만들어 줍니다. 

public class CustomEditText extends AppCompatEditText {

    public CustomEditText(Context context) {
        super(context);
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection conn = super.onCreateInputConnection(outAttrs);
        outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
        return conn;
    }
}


레이아웃 xml 에서 커스텀 eidtText를 정의합니다.

<com.myApplication.CustomEditText
	android:id="@+id/search_view"
	android:layout_width="match_parent"
	android:layout_height="?attr/actionBarSize"
	android:layout_weight="1"
	android:background="@android:color/transparent"
	android:gravity="center_vertical"
	android:hint="@string/txt_search"
	android:imeOptions="actionSearch"
	android:maxLines="1"
	android:paddingLeft="2dp"
	android:textColor="@color/colorActionBar"
	android:textColorHint="@color/colorActionBar" />


마지막으로 Java 코드 내에서 

EditText search_view = (EditText) findViewbyId(R.id.search_view);
search_view.setOnEditorActionListener(new TextView.OnEditorActionListener() {
	@Override
	public boolean onEditorAction(TextView textView, int keyCode, KeyEvent event) {
		Log.e("test","keyEvent "+keyCode);
		// TODO :: work for
		if(keyCode == event.KEYCODE_HOME){
			loadAddress();
		}
		return false;
	}
});


개발할때 간단한 아이콘은 어디서 긁어다 쓰고 싶지만 저작권 문제도 있고, 

잉크스페이스 같은 툴로 svg 파일로 시도를 해보았지만

내용들이 전부 base64 로 인코딩 되어있어 xml 로 사용하지 못하였습니다. 

그래서 찾다보니 이런게 있어서 공유합니다.  


참고 url

https://developers-kr.googleblog.com/2015/10/androidstudio14.html



1. File > New > Vector Asset 메뉴로 이동합니다.




2. icon 옆에 캐릭터를 클릭을 하면 3번과 같이 사용 가능 리스트가 나옵니다.




3. 원하는 이미지를 선택합니다.


완성


깔끔하게 만들어진 벡터이미지를 확인 할수 있습니다.


메뉴상 local file (svg, psd) 라고 있는 걸로 봐서는 다른것도 가능할 듯 싶으나,

저는 포토샵이 없어 위의 original 파일이 없는 관계로 테스트는 못해봤습니다.


- 끝 -

앱 링크 URL

https://play.google.com/store/apps/details?id=com.poisonrose.finedust



  

 



간단하게 미세먼지 정보와 날씨 정보를 조회합니다.


시작시 현재 위치를 스캔하여 미세먼지 정보와 날씨 정보를 가져옵니다.


공공데이터포털 3.0 에서 데이터를 사용하였으며

미세먼지 정보는

에어코리아


날씨 정보는

기상청에서 데이터를 가져옵니다.


메인 원을 클릭시 해당하는 값의 상세 설명이 나옵니다.


돋보기 모양을 누르시면 주소 검색을 통한 타지역 정보를 가져올수 있습니다.


기타 오류사항이나 문의사항은 메일 주세요


스윙 보트

Swing Vote, 2008

장르 : 코미디


...




앱 링크 URL

https://play.google.com/store/apps/details?id=com.poisonrose.screenvideo




간편하고 가볍게 화면 스크린을 녹화 할 수 있습니다.

저장되는 파일은 외부메모리에 저장을 하며, 안전하고 쉽게 파일 관리 할 수 있습니다.


상단 우측 + 버튼을 누르면 스크린을 저장을 시작합니다.


종료를 할려면 플로팅 Ui에서 정지 버튼을 누릅니다.


플로팅 UI 상단 버튼은 UI를 이동 할 수 있습니다.


플로팅 UI 하단 버튼은 현재 앱을 다시 실행 합니다.


캡쳐된 동영상은 앱 메인 리스트에 표시 되며,

핸드폰 내의 기본 동영상 플레이어로 실행 합니다.


메인리스트 우측의 공유 버튼을 누르시면 해당 파일을 공유할 수 있습니다.


메인에서 상단 돋보기 아이콘을 클릭하시면 

현재 저장된 동영상을 검색 할 수 있습니다.


기타 오류사항이나 문의사항은 메일 주세요

앱 APK 에 특정권한(android.permission.RECORD_AUDIO) 를 사용합니다.


이는 앱의 특징인 화면 레코딩을 위해 MediaRecorder 오브젝트의 사용을 위해 위의 특정권한을 사용합니다.


모든 데이터는 해당 단말기 내의 Local에서 외부 메모리에서만 저장되며,


이외 모든 개인정보에 관련된 데이터 수집, 사용, 공유는 일체 사용하지 않습니다.


기타 문의사항은 메일로 주면 됩니다.



내 앱의 캐쉬 데이터 지우기 예제 입니다.

getCacheDir() 에서부터 정보를 불러와서 Folder 스캔하여 아래에 있는 파일을 삭제 하는 방식입니다.

적당히 남겨두어야 하는 부분은 주석을 참조하여 사용하시면 됩니다.


/**
 * 앱 캐시 지우기
 * @param context
 */
public static void clearApplicationData(Context context) {
	File cache = context.getCacheDir();
	File appDir = new File(cache.getParent());
	if (appDir.exists()) {
		String[] children = appDir.list();
		for (String s : children) {
			//다운로드 파일은 지우지 않도록 설정
			//if(s.equals("lib") || s.equals("files")) continue;
			deleteDir(new File(appDir, s));
			Log.d("test", "File /data/data/"+context.getPackageName()+"/" + s + " DELETED");
		}
	}
}

private static boolean deleteDir(File dir) {
	if (dir != null && dir.isDirectory()) {
		String[] children = dir.list();
		for (int i = 0; i < children.length; i++) {
			boolean success = deleteDir(new File(dir, children[i]));
			if (!success) {
				return false;
			}
		}
	}
	return dir.delete();
}


Directory 생성, file list 가져오기 예제입니다.

여기 예제는 외부 메모리를 엑세스 하기때문에 

os 7.0 이상에서는 system permission 관련 로직을 추가하셔야 가능합니다.


참고 url 

https://developer.android.com/reference/android/os/Environment.html


public static String DIRECTORY_ALARMS = "alarms";

public static String DIRECTORY_DCIM = "DICM";

public static String DIRECTORY_DOCUMENTS = "documents" ;

public static String DIRECTORY_DOWNLOADS = "downloads" ;

public static String DIRECTORY_MOVIES = "movies";

public static String DIRECTORY_MUSIC = "music" ;

public static String DIRECTORY_NOTIFICATIONS = "notifications" ;

public static String DIRECTORY_PICTURES = "pictures" ;

public static String DIRECTORY_PODCASTS = "podcasts" ;

public static String DIRECTORY_RINGTONES = "ringtones" ;


public void getFolderFileList() {
	//internal 
	//File dir = new File(getFilesDir().getAbsolutePath(), "test");
	File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "test");
	if (! dir.isDirectory()){
		if (! dir.mkdirs()){
		}
	}

	String[] str = dir.list();
	for(String st : str) {
		Log.d("test"," st : "+st +" , "+st.endsWith(".gif"));
	}

	File[] files = dir.listFiles();
	for(File f : files) {
		Log.w("test"," f : "+f.getPath() +" , "+f.getPath().endsWith(".gif"));
		Log.i("test"," f : "+f.getName() +" , "+f.getName().endsWith(".gif"));
	}
}


결과화면



+ Recent posts