build.gradle (Module: app)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.phaisarn.myapplication" minSdkVersion 21 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 { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:26.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'com.koushikdutta.ion:ion:2.+' implementation 'com.github.armcha:SimplePermissions:1.0.0' } |
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.phaisarn.myapplication"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <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" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> <!-- <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@android:drawable/ic_dialog_email"/> --> </android.support.design.widget.CoordinatorLayout> |
content_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".MainActivity" tools:showIn="@layout/activity_main"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content"></ListView> </LinearLayout> |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
package com.phaisarn.myapplication; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.app.ProgressDialog; import android.os.Environment; import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.Toast; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.koushikdutta.async.future.FutureCallback; import com.koushikdutta.ion.Ion; import com.luseen.simplepermission.permissions.Permission; import com.luseen.simplepermission.permissions.PermissionActivity; import com.luseen.simplepermission.permissions.SinglePermissionCallback; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; public class MainActivity extends PermissionActivity { private Button mButtonClicked; //เก็บสถานะว่ากำลังโหลดหรือไม่ เพื่อป้องกันการคลิกซ้ำ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); /* FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); */ //แสดง ProgressDialog ขณะกำลังโหลดรายชื่อไฟล์จากเซิร์ฟเวอร์ final ProgressDialog dialog = new ProgressDialog(this); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.setMessage("Please Wait..."); dialog.setIndeterminate(true); dialog.show(); //ส่ง request ไปอ่านรายชื่อไฟล์ final ArrayList<CustomItem> itemArray = new ArrayList<>(); Ion.with(MainActivity.this) .load("http://10.0.2.2/pro-android/list-file.php") .asJsonArray() .setCallback(new FutureCallback<JsonArray>() { @Override public void onCompleted(Exception e, JsonArray result) { dialog.dismiss(); JsonObject jsonObject; for (int i = 0; i < result.size(); i++) { CustomItem item = new CustomItem(); jsonObject = (JsonObject) result.get(i); item.fileName = jsonObject.get("name").getAsString(); item.fileSize = jsonObject.get("size").getAsString(); itemArray.add(item); } CustomAdapter adapter = new CustomAdapter(getBaseContext(), itemArray); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); //ดักอีเวนต์เมื่อคลิกที่ป่มบนแต่ละรายการของ ListView ซึ่งสร้าง Custom Listener ไว้แล้ว adapter.setOnButtonClickListener(new CustomAdapter.OnButtonClickListener() { @Override public void onButtonClick(final View v, final ProgressBar pb) { mButtonClicked = (Button) v; checkPermission(v, pb); //ตรวจสอบ Runtime Permission } }); } }); } private void checkPermission(final View v, final ProgressBar pb) { requestPermission(Permission.WRITE_EXTERNAL_STORAGE, new SinglePermissionCallback() { @Override public void onPermissionResult(boolean granted, boolean isDeniedForever) { if (granted) { try { downloadFile(v.getTag().toString(), pb); //อ่านชื่อไฟล์ที่แนบไว้ในแบบ Tag กับแต่ละปุ่ม } catch (MalformedURLException ex) { } } else { Toast.makeText(getBaseContext(), "ถ้าไม่อนุญาต จะไม่สามารถบันทึกไฟล์ได้", Toast.LENGTH_LONG).show(); } } }); } private void downloadFile(String fileName, final ProgressBar pb) throws MalformedURLException { mButtonClicked.setEnabled(!mButtonClicked.isEnabled()); //สลับสถานะของปุ่ม เพื่อป้องกันการคลิกซ้ำ pb.setIndeterminate(false); pb.setVisibility(View.VISIBLE); //ตามปกติ เราจะซ่อน ProgressฺBar ไว้ก่อน และเมื่อโหลดไฟล์จึงจะแสดง String url = "http://10.0.2.2/pro-android/download/" + fileName; //URL ของไฟล์ที่จะโหลด Ion.with(this) .load(url) .progressBar(pb) //ProgressBar บนแต่ละรายการ .write(getNewFile(url)) //ดูเมธอด getNewFile() .setCallback(new FutureCallback<File>() { @Override public void onCompleted(Exception e, File file) { pb.setVisibility(View.INVISIBLE); //เมื่อโหลดเสร็จ ให้ซ่อน Progress mButtonClicked.setEnabled(!mButtonClicked.isEnabled()); //สลับสถานะของปุ่ม } }); } private File getNewFile(String fileUrl) throws MalformedURLException { URL url = new URL(fileUrl); String path = url.getPath(); int lastSeparatorIndex = path.lastIndexOf("/"); //หาตำแหน่งของเครื่องหมาย "/" อันสุดท้าย String fileName = path.substring(lastSeparatorIndex + 1); //อ่านชื่อไฟล์ซึ่งอยู่ถัดจาก "/" อันสุดท้าย String fileParts[] = fileName.split("\\."); //คัดแยกชื่อไฟล์และส่วนขยายออกจากกันด้วย "." String name = fileParts[0]; String ext = fileParts[1]; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { //จะให้เก็บไว้ที่ไดเร็กทอรี Downloads ของ External Storage File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); String absPath = file.getAbsolutePath(); //ลองสร้างอินสแตนซ์ของไฟล์ที่มีชื่อเดียวกับไฟล์ที่ดาวน์โหลด File newFile = new File(absPath + "/" + fileName); //ตรวจสอบว่าไฟล์ที่สร้างขึ้น ซ้ำกับที่มีอยู่แล้วหรือไม่ //ถ้าซ้ำให้เพิ่มตัวเลขต่อท้าย แล้วตรวจสอบใหม่ๆ //ทำเช่นนี้ไปจนกว่าชื่อไฟล์จะไม่ซ้ำ int count = 1; while (newFile.exists()) { String suffix = "-" + count; newFile = new File(absPath + "/" + name + suffix + "." + ext); //เช่น path/file-1.zip count++; } return newFile; } else { return new File(getApplicationContext().getFilesDir().getAbsolutePath() + "/" + fileName); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } |
บรรทัดที่ 58 : เรียกไปที่ http://10.0.2.2/pro-android/list-file.php
layout/item_layout.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?xml version="1.0" encoding="utf-8"?> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:columnCount="3" android:padding="8dp"> <ImageView android:id="@+id/image_view" android:layout_gravity="top" android:layout_marginRight="8dp" android:layout_rowSpan="3" android:src="@drawable/ic_file" android:tint="@color/colorAccent" /> <TextView android:id="@+id/text_filename" android:layout_columnWeight="1" android:layout_gravity="fill_horizontal" android:textColor="#00c" android:textSize="18sp" /> <Button android:id="@+id/button_download" android:layout_rowSpan="3" android:text="Download" /> <TextView android:id="@+id/text_filesize" android:layout_columnWeight="1" android:layout_gravity="fill_horizontal" android:textSize="14sp" /> <ProgressBar android:id="@+id/progress" style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:layout_columnWeight="1" android:layout_gravity="fill_horizontal" android:layout_marginRight="30dp" android:indeterminate="true" android:visibility="invisible" /> </GridLayout> |
CustomAdapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
package com.phaisarn.myapplication; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ProgressBar; import java.util.ArrayList; public class CustomAdapter extends ArrayAdapter { private Context mContext; private ArrayList<CustomItem> mItems; public CustomAdapter(Context context, ArrayList<CustomItem> items) { super(context, 0, items); mContext = context; mItems = items; } interface OnButtonClickListener { void onButtonClick(View v, ProgressBar pb); } private OnButtonClickListener mListener; public void setOnButtonClickListener(OnButtonClickListener listener) { mListener = listener; } @Override public View getView(int position, View convertView, ViewGroup parent) { final CustomHolder vHolder; if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(mContext); convertView = inflater.inflate(R.layout.item_layout, parent, false); vHolder = new CustomHolder(convertView); convertView.setTag(vHolder); } else { vHolder = (CustomHolder) convertView.getTag(); } CustomItem item = mItems.get(position); vHolder.textFileName.setText(item.fileName); vHolder.textFileSize.setText(item.fileSize); vHolder.button.setTag(item.fileName); vHolder.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mListener.onButtonClick(v, vHolder.progressBar); } }); return convertView; } } |
CustomHolder.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.phaisarn.myapplication; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; public class CustomHolder { public TextView textFileName; public TextView textFileSize; public Button button; public ProgressBar progressBar; public CustomHolder(View v) { textFileName = (TextView) v.findViewById(R.id.text_filename); textFileSize = (TextView) v.findViewById(R.id.text_filesize); button = (Button) v.findViewById(R.id.button_download); progressBar = (ProgressBar) v.findViewById(R.id.progress); } } |
CustomItem.java
1 2 3 4 5 6 |
package com.phaisarn.myapplication; public class CustomItem { public String fileName; public String fileSize; } |
รูปที่ใช้