2014年10月1日 星期三

Android automatic send e-mail 自動發送e-mail

也就是不需經過使用者即可發送email,是寄件者(開發者)寄送給其他收件者的方法
1.安裝jar檔
mail.jar
activation.jar
additionnal.jar
 將這三個檔案下載到專案的libs資料夾上,接著在eclips重新整理專案,在這三個檔案按右鍵->Bulid Path-> Add to Build Path 完成
 2.在src新增建立資料夾com,在資料夾com裡面再建立一個資料夾provider,接著再eclips裡此資料夾下建立 JSSEProvider.java
 
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Alexander Y. Kleymenov
 * @version $Revision$
 */


import java.security.AccessController;
import java.security.Provider;

public final class JSSEProvider extends Provider {

    public JSSEProvider() {
        super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
        AccessController.doPrivileged(new java.security.PrivilegedAction() {
            public Void run() {
                put("SSLContext.TLS",
                        "org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
                put("Alg.Alias.SSLContext.TLSv1", "TLS");
                put("KeyManagerFactory.X509",
                        "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
                put("TrustManagerFactory.X509",
                        "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
                return null;
            }
        });
    }
}
3.在自己的專案src資料夾下建立GMailSender.java
import javax.activation.DataHandler;   
import javax.activation.DataSource;   
import javax.mail.Message;   
import javax.mail.PasswordAuthentication;   
import javax.mail.Session;   
import javax.mail.Transport;   
import javax.mail.internet.InternetAddress;   
import javax.mail.internet.MimeMessage;   

import android.util.Log;

import java.io.ByteArrayInputStream;   
import java.io.IOException;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.security.Security;   
import java.util.Properties;   

public class GMailSender extends javax.mail.Authenticator {   
    private String mailhost = "smtp.gmail.com";   
    private String user;   
    private String password;   
    private Session session;   

    static {   
        Security.addProvider(new com.provider.JSSEProvider());   
    }  

    public GMailSender(String user, String password) {   
        this.user = user;   
        this.password = password;   
        Log.i("user::::::::", user);
        Log.i("password::::::::", password);
        Properties props = new Properties();   
        props.setProperty("mail.transport.protocol", "smtp");   
        props.setProperty("mail.host", mailhost);   
        props.put("mail.smtp.auth", "true");   
        props.put("mail.smtp.port", "465");   
        props.put("mail.smtp.socketFactory.port", "465");   
        props.put("mail.smtp.socketFactory.class",   
                "javax.net.ssl.SSLSocketFactory");   
        props.put("mail.smtp.socketFactory.fallback", "false");   
        props.setProperty("mail.smtp.quitwait", "false");   

        session = Session.getDefaultInstance(props, this);   
    }   

    protected PasswordAuthentication getPasswordAuthentication() {   
        return new PasswordAuthentication(user, password);   
    }   

    public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {   
        try{
          Log.i("subject::::::::", subject);
             Log.i("body::::::::", body);
             Log.i("sender::::::::", sender);
             Log.i("recipients::::::::", recipients);
        MimeMessage message = new MimeMessage(session);   
        DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));   
        message.setSender(new InternetAddress(sender));   
        message.setSubject(subject);   
        message.setDataHandler(handler);  

        /*******2015.09.29 附加圖片*******/
        // create multipart
        Multipart multipart = new MimeMultipart();

        // create bodypart with image and set content-id
        MimeBodyPart messageBodyPart = new MimeBodyPart();
        File testImage = new File("/storage/emulated/0/Download/", "test.png"); //手機檔案位置,檔案名稱
        DataSource source = new FileDataSource(testImage);
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName("image.png");
        messageBodyPart.setDisposition(MimeBodyPart.INLINE);
        messageBodyPart.setHeader("Content-ID","");
        multipart.addBodyPart(messageBodyPart);

        // create bodypart with html content and reference to the content-id
        messageBodyPart = new MimeBodyPart();
        String htmlText = "";
        messageBodyPart.setContent(htmlText, "text/html");
        multipart.addBodyPart(messageBodyPart);

        // add multipart to message
        message.setContent(multipart);
        
        /***************參考來源 http://goo.gl/OAZn5q ****************/

 
        if (recipients.indexOf(',') > 0)   
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));   
        else  
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));   
        Transport.send(message);   
        }catch(Exception e){
          Log.i("eeeeee::::::::", e+"");
        }
    }   

    public class ByteArrayDataSource implements DataSource {   
        private byte[] data;   
        private String type;   

        public ByteArrayDataSource(byte[] data, String type) {   
            super();   
            this.data = data;   
            this.type = type;   
        }   

        public ByteArrayDataSource(byte[] data) {   
            super();   
            this.data = data;   
        }   

        public void setType(String type) {   
            this.type = type;   
        }   

        public String getContentType() {   
            if (type == null)   
                return "application/octet-stream";   
            else  
                return type;   
        }   

        public InputStream getInputStream() throws IOException {   
            return new ByteArrayInputStream(data);   
        }   

        public String getName() {   
            return "ByteArrayDataSource";   
        }   

        public OutputStream getOutputStream() throws IOException {   
            throw new IOException("Not Supported");   
        }   
    }   
}  
4.接著在想觸發的檔案上加上此段code
try {   
                    GMailSender sender = new GMailSender("username@gmail.com", "password"); //寄件者(開發方)帳號與密碼
                    sender.sendMail("This is Subject",   //信件標題
                            "This is Body",   //信件內容
                            "username@gmail.com",   //寄件者
                            "user@yahoo.com");   //收件者
                } catch (Exception e) {   
                    Log.e("SendMail", e.getMessage(), e);   
                } 
5.可能發生問題在 Log.i("eeeeee::::::::", e+"");
A.android.os.NetworkOnMainThreadException 在MainActivity.java加上此段
/* 加入StrictMode避免發生 android.os.NetworkOnMainThreadException */
  StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectDiskReads().detectDiskWrites().detectNetwork()
    .penaltyLog().build());
B.javax.mail.AuthenticationFailedException 此為無法登入帳號,可能是帳號密碼錯誤,或是帳號未設定 此時GMail會收到信,點選連結變更設定
https://www.google.com/settings/security/lesssecureapps

 啟動IMAP
 完成
 參考資料:
http://stackoverflow.com/questions/2020088/sending-email-in-android-using-javamail-api-without-using-the-default-built-in-a/2033124#2033124

http://stackoverflow.com/questions/1334802/how-can-i-use-external-jars-in-an-android-project/6859020#6859020

http://bbs.csdn.net/topics/310000387

http://stackoverflow.com/questions/7398382/embedd-inline-images-using-java-mail-in-android

檔案位置:https://github.com/terryyamg/AutoEmail

2014年9月16日 星期二

Android Action Bar search 搜尋

前陣子製作側邊欄,參考[Android]使用 Navigation Drawer 製作側選單(1)與[Android]使用 Navigation Drawer 製作側選單(2) ,由於上方有個ActionBar,在這裡面介紹的是按鈕,由於想用類似搜尋功能,於是稍作修改,主要修改地方為"Action Button 建立及點選事件"
1.在資料夾res->menu->main.xml修改



2.在資料夾res->layout->建立search_layout.xml 一個編輯,一個按鈕


    
    

    


3.MainActivity.java修改
 private EditText searchName;
 private String searchObject;
 private Button searchButton;
 // ================================================================================
 // Action Button 建立及點選事件
 // ================================================================================
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  View v = (View) menu.findItem(R.id.action_search).getActionView();
  //文字編輯部分
  searchName = (EditText) v.findViewById(R.id.search);
  searchName.addTextChangedListener(new TextWatcher() {
   @Override
   public void afterTextChanged(Editable s) {
    // TODO Auto-generated method stub
   }

   @Override
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before,
     int count) {
    // TODO Auto-generated method stub
    try {
     searchObject = searchName.getText().toString(); // 取得輸入文字
    } catch (Exception e) {

    }

   }
  });
  //送出部分
  searchButton = (Button) v.findViewById(R.id.searchGo);
  searchButton.setOnClickListener(new Button.OnClickListener() {
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    goSearch(); //要做甚麼
   }
  });

  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {

  // home
  if (drawerToggle.onOptionsItemSelected(item)) {
   return true;
  }

  // action buttons
  switch (item.getItemId()) {
  //用不到,刪掉
  default:
   break;
  }

  return super.onOptionsItemSelected(item);
 }

 void goSearch() {
  if (searchObject == null) {
  //搜尋空值,不做事
  } else {
   Intent intent = new Intent(this, xxxx.class); //前進至xxxx頁面
   intent.putExtra("searchName", searchObject); //傳值
   startActivity(intent); //啟動出發
  }
 }




參考來源:http://blog.stylingandroid.com/basic-actionbar-part5/
http://wptrafficanalyzer.in/blog/adding-custom-action-view-to-action-bar-in-android/

2014年8月21日 星期四

Android Typeface 字型

更改app上的文字字型
首先要下載中文字型
http://lms.ltu.edu.tw/sys/read_attach.php?id=675313
http://lms.ltu.edu.tw/sys/read_attach.php?id=675314
另外提供一個英文字型網站(英文字型多,中文字少)
http://www.fontsaddict.com/

下載完畢後,解壓縮,選擇想要的字型檔(.ttf)
然後再專案裡的assets建立fonts資料夾
想要的字型檔放入fonts資料夾




使用 Typeface 指令即可修改字型
1.activity_main.xml



2.MainActivity.java
 private TextView output;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  /* 字型 */
  Typeface fontch = Typeface.createFromAsset(getAssets(), "fonts/wt034.ttf");
  output = (TextView) findViewById(R.id.output);
  output.setTypeface(fontch);

 }
參考來源:http://key.chtouch.com/ContentView.aspx?P=216
http://abgne.tw/android/android-code-snippets/android-using-custom-fonts.html

2014年8月15日 星期五

Android ViewPager 前導 說明頁

使用app時會出現前導說明頁
整體流程如下圖
首先要修改先前開機畫面SplashScreen.java檔啟動位置
1.SplashScreen.java
 Intent i = new Intent(SplashScreen.this, Leading.class); //啟動完開機畫面SplashScreen.java後跳轉Leading.java
2.加入Leading.java在AndroidManifest.xml

        
        
3.建立leading.xml,lay1.xml,lay2.xml,lay3.xml
 leading.xml 下方的圓點


    

    

        

        

        
    


lay1.xml 說明頁的第一張圖pic1


    


lay2.xml 說明頁的第二張圖pic2


    



lay3.xml 說明頁的第三張圖pic3+開始體驗Button


    

    


4.Leading.java 說明頁面程式碼
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

public class Leading extends Activity {
 private ViewPager myViewPager; // 頁卡內容
 private List list; // 存放頁卡
 private TextView dot1, dot2, dot3; // 這些點都是文字
 private Button startButton; // 按鈕,開始體驗

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.leading);
  initDot();
  initViewPager();
 }

 private void initDot() {
  dot1 = (TextView) this.findViewById(R.id.textView1); // 這些點都是文字
  dot2 = (TextView) this.findViewById(R.id.textView2);
  dot3 = (TextView) this.findViewById(R.id.textView3);
 }

 private void initViewPager() {
  myViewPager = (ViewPager) this.findViewById(R.id.viewPager);
  list = new ArrayList();

  LayoutInflater inflater = getLayoutInflater();

  View view = inflater.inflate(R.layout.lay3, null); // 只是為了等下findviewbuid而獨立拿出來賦給view

  list.add(inflater.inflate(R.layout.lay1, null));
  list.add(inflater.inflate(R.layout.lay2, null));
  list.add(view);
  try {
   myViewPager.setAdapter(new MyPagerAdapter(list));

   myViewPager.setOnPageChangeListener(new MyPagerChangeListener());
  } catch (NullPointerException e) {
  }
  startButton = (Button) view.findViewById(R.id.start); // 與上面對應,獲取這個按鈕

  startButton.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {
    Intent intent = new Intent(Leading.this, MainActivity.class); //按下按鈕後跳轉至MainActivity.java

    startActivity(intent);

   }
  });
 }

 class MyPagerAdapter extends PagerAdapter {
  public List mListViews;

  public MyPagerAdapter(List mListViews) {
   this.mListViews = mListViews;
  }

  @Override
  public void destroyItem(View arg0, int arg1, Object arg2) {
   ((ViewPager) arg0).removeView(mListViews.get(arg1));
  }

  @Override
  public void finishUpdate(View arg0) {
  }

  @Override
  public int getCount() {
   return mListViews.size();
  }

  @Override
  public Object instantiateItem(View arg0, int arg1) {
   ((ViewPager) arg0).addView(mListViews.get(arg1), 0);
   return mListViews.get(arg1);
  }

  @Override
  public boolean isViewFromObject(View arg0, Object arg1) {
   return arg0 == (arg1);
  }

  @Override
  public void restoreState(Parcelable arg0, ClassLoader arg1) {
  }

  @Override
  public Parcelable saveState() {
   return null;
  }

  @Override
  public void startUpdate(View arg0) {
  }
 }

 class MyPagerChangeListener implements OnPageChangeListener {

  @Override
  public void onPageSelected(int arg0) {
   // TODO Auto-generated method stub
   switch (arg0) { // 設置點的顏色
   case 0:
    dot1.setTextColor(Color.WHITE);
    dot2.setTextColor(Color.BLACK);
    dot3.setTextColor(Color.BLACK);
    break;

   case 1:
    dot1.setTextColor(Color.BLACK);
    dot2.setTextColor(Color.WHITE);
    dot3.setTextColor(Color.BLACK);
    break;

   case 2:
    dot1.setTextColor(Color.BLACK);
    dot2.setTextColor(Color.BLACK);
    dot3.setTextColor(Color.WHITE);
    break;

   }
  }

  @Override
  public void onPageScrollStateChanged(int arg0) {
   // TODO Auto-generated method stub

  }

  @Override
  public void onPageScrolled(int arg0, float arg1, int arg2) {
   // TODO Auto-generated method stub

  }

 }
}

參考來源:http://blog.segmentfault.com/zhongbaitu/1190000000398209

2014年8月4日 星期一

Android 上架至 google play 問題

基本上參考此連結http://xyz.cinc.biz/2013/06/android-app.html
寫得很清楚,但步驟3完會出現strings.xml錯誤的訊息

找了一下

點選eclipse的 "Window" > "Preferences" > "Android" > "Lint Error Checking"
找到 Id欄 是"MissingTranslation" 點選後Erroe修改成Warning就可以了

參考連結:
http://stackoverflow.com/questions/11443996/lint-how-to-ignore-key-is-not-translated-in-language-errors

2014年7月18日 星期五

Android share 分享功能

分享至facebook,line之類的
1.activity_main.xml
建立一個分享按鈕

2.MainActivity.java
 private Button shareButton;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  shareButton = (Button) findViewById(R.id.shareButton);
  shareButton.setOnClickListener(new Button.OnClickListener() {
   public void onClick(View v) {
    shareDialog();
   }
  });
 }

 // 分享app
 void shareDialog() {
  
  String shareText = "與你分享的快樂勝過獨自擁有"; //
  //Uri imageUri = Uri.parse("android.resource://" + getPackageName() + "/drawable/" + "ic_launcher"); //分享圖片至gmail、twitter可,line、facebook不行
  //Log.i("imageUri:", imageUri + "");
   Intent shareIntent = new Intent();
   shareIntent.setAction(Intent.ACTION_SEND);
   shareIntent.setType("text/plain"); //文字檔類型
   shareIntent.putExtra(Intent.EXTRA_TEXT, shareText); //傳送文字
   shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
   //shareIntent.setType("image/png"); //圖片png檔類型
  // shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri); //傳送圖片
   startActivity(Intent.createChooser(shareIntent, "分享"));
 }
參考來源:
http://hscc.cs.nctu.edu.tw/~lincyu/Android/ShareText.pdf
http://stackoverflow.com/questions/20333186/how-to-share-imagetext-using-action-send-in-android

2014年7月9日 星期三

Android google map 結合 spinner 下拉式選單

利用下拉式選單選定某個項目後,直接在google map上移到定點
1.activity_main.xml 在地圖下方加入下拉式選單




2.MainActivity.java 以基隆市、台北市85度c為例
private float coordinate[][][] = {
   { { 0, (float) 25.1288160, (float) 121.7404860 },
     { 0, (float) 25.1286310, (float) 121.7594120 },
     { 0, (float) 25.0972380, (float) 121.7122780 } },
   { { 1, (float) 25.0574640, (float) 121.5554840 },
     { 1, (float) 24.9836380, (float) 121.5673320 },
     { 1, (float) 25.0310780, (float) 121.5522090 } },
    }; //設定定點座標
private String[] type = new String[] { "基隆市", "台北市" }; //第一個選單
 private String[] locationName = new String[] { "85度c基隆孝一店", "85度c基隆東信店","85度c七堵明德店" }; //初始選單
 private String[][] type2 = new String[][] {
   { "85度c基隆孝一店", "85度c基隆東信店", "85度c七堵明德店" },
   { "85度c台北光復北店", "85度c木柵木新店", "85度c台北安和店" } };//第二個選單
 private Spinner sp1;// 第一個下拉選單
 private Spinner sp2;// 第二個下拉選單
 private Context context;
 ArrayAdapter adapter;
 ArrayAdapter adapter2;
@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  context = this;
  // 程式剛啟動時載入第一個下拉選單
  adapter = new ArrayAdapter(this,
    android.R.layout.simple_spinner_item, type);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  sp1 = (Spinner) findViewById(R.id.type);
  sp1.setAdapter(adapter); //將adapter加入sp1選單
  sp1.setOnItemSelectedListener(selectListener); //第一個選單監聽動作

  // 程式剛啟動時載入第一個下拉選單,所以第二選單要載入第一個選單的子項目
  adapter2 = new ArrayAdapter(this,
    android.R.layout.simple_spinner_item, locationName);
  adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  sp2 = (Spinner) findViewById(R.id.type2);
  sp2.setAdapter(adapter2); //將adapter加入sp2選單
  sp2.setOnItemSelectedListener(selectListener2); //第二個選單監聽動作
 }

 // 第一個下拉選單監聽動作
 private OnItemSelectedListener selectListener = new OnItemSelectedListener() {
  public void onItemSelected(AdapterView parent, View v, int position,
    long id) {
   // 讀取第一個下拉選單是選擇第幾個
   int pos = sp1.getSelectedItemPosition();
   // 重新產生新的Adapter,用的是二維陣列type2[pos]
   adapter2 = new ArrayAdapter(context,
     android.R.layout.simple_spinner_item, type2[pos]);
   // 載入第二個下拉選單Spinner
   sp2.setAdapter(adapter2);

  }

  public void onNothingSelected(AdapterView arg0) {

  }

 };

 // 第二個下拉選單監聽動作
 private OnItemSelectedListener selectListener2 = new OnItemSelectedListener() {
  public void onItemSelected(AdapterView parent, View v, int position,
    long id) {
   setMapLocation(); //移動到選定項目位置的動作
  }

  public void onNothingSelected(AdapterView arg0) {

  }

 };

 public void setMapLocation() {
  try {
   int iSelect1 = sp1.getSelectedItemPosition(); //第一個下拉選單被選到的第幾個項目
   int iSelect2 = sp2.getSelectedItemPosition(); //第二個下拉選單被選到的第幾個項目
   double dLat = coordinate[iSelect1][iSelect2][1]; // 抓取座標,南北緯
   double dLon = coordinate[iSelect1][iSelect2][2]; // 抓取座標,東西經
   LatLng gg = new LatLng(dLat, dLon); //將座標位置輸入設定
   map.moveCamera(CameraUpdateFactory.newLatLng(gg)); //移動到定點
   map.animateCamera(CameraUpdateFactory.zoomTo(15)); //縮放
  } catch (NullPointerException e) {
   Log.i("myLocation", "NullPointException");
  }
 }
參考來源: http://jim690701.blogspot.tw/2012/07/androidspinner_15.html